OLD | NEW |
(Empty) | |
| 1 # -*- coding: ascii -*- |
| 2 # |
| 3 # Util/asn1.py : Minimal support for ASN.1 DER binary encoding. |
| 4 # |
| 5 # =================================================================== |
| 6 # The contents of this file are dedicated to the public domain. To |
| 7 # the extent that dedication to the public domain is not available, |
| 8 # everyone is granted a worldwide, perpetual, royalty-free, |
| 9 # non-exclusive license to exercise all rights associated with the |
| 10 # contents of this file for any purpose whatsoever. |
| 11 # No rights are reserved. |
| 12 # |
| 13 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 14 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 15 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 16 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| 17 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| 18 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 19 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 20 # SOFTWARE. |
| 21 # =================================================================== |
| 22 |
| 23 from Crypto.Util.number import long_to_bytes, bytes_to_long |
| 24 import sys |
| 25 from Crypto.Util.py3compat import * |
| 26 |
| 27 __all__ = [ 'DerObject', 'DerInteger', 'DerOctetString', 'DerNull', 'DerSequence
', 'DerObjectId' ] |
| 28 |
| 29 class DerObject: |
| 30 """Base class for defining a single DER object. |
| 31 |
| 32 Instantiate this class ONLY when you have to decode a DER element. |
| 33 """ |
| 34 |
| 35 # Known TAG types |
| 36 typeTags = { 'SEQUENCE': 0x30, 'BIT STRING': 0x03, 'INTEGER': 0x02, |
| 37 'OCTET STRING': 0x04, 'NULL': 0x05, 'OBJECT IDENTIFIER': 0x06 } |
| 38 |
| 39 def __init__(self, ASN1Type=None, payload=b('')): |
| 40 """Initialize the DER object according to a specific type. |
| 41 |
| 42 The ASN.1 type is either specified as the ASN.1 string (e.g. |
| 43 'SEQUENCE'), directly with its numerical tag or with no tag |
| 44 at all (None).""" |
| 45 if isInt(ASN1Type) or ASN1Type is None: |
| 46 self.typeTag = ASN1Type |
| 47 else: |
| 48 if len(ASN1Type)==1: |
| 49 self.typeTag = ord(ASN1Type) |
| 50 else: |
| 51 self.typeTag = self.typeTags.get(ASN1Type) |
| 52 self.payload = payload |
| 53 |
| 54 def isType(self, ASN1Type): |
| 55 return self.typeTags[ASN1Type]==self.typeTag |
| 56 |
| 57 def _lengthOctets(self, payloadLen): |
| 58 """Return a byte string that encodes the given payload length (i
n |
| 59 bytes) in a format suitable for a DER length tag (L). |
| 60 """ |
| 61 if payloadLen>127: |
| 62 encoding = long_to_bytes(payloadLen) |
| 63 return bchr(len(encoding)+128) + encoding |
| 64 return bchr(payloadLen) |
| 65 |
| 66 def encode(self): |
| 67 """Return a complete DER element, fully encoded as a TLV.""" |
| 68 return bchr(self.typeTag) + self._lengthOctets(len(self.payload)
) + self.payload |
| 69 |
| 70 def _decodeLen(self, idx, der): |
| 71 """Given a (part of a) DER element, and an index to the first by
te of |
| 72 a DER length tag (L), return a tuple with the payload size, |
| 73 and the index of the first byte of the such payload (V). |
| 74 |
| 75 Raises a ValueError exception if the DER length is invalid. |
| 76 Raises an IndexError exception if the DER element is too short. |
| 77 """ |
| 78 length = bord(der[idx]) |
| 79 if length<=127: |
| 80 return (length,idx+1) |
| 81 payloadLength = bytes_to_long(der[idx+1:idx+1+(length & 0x7F)]) |
| 82 if payloadLength<=127: |
| 83 raise ValueError("Not a DER length tag.") |
| 84 return (payloadLength, idx+1+(length & 0x7F)) |
| 85 |
| 86 def decode(self, derEle, noLeftOvers=0): |
| 87 """Decode a complete DER element, and re-initializes this |
| 88 object with it. |
| 89 |
| 90 @param derEle A complete DER element. It must start with a
DER T |
| 91 tag. |
| 92 @param noLeftOvers Indicate whether it is acceptable to complet
e the |
| 93 parsing of the DER element and find that not
all |
| 94 bytes in derEle have been used. |
| 95 @return Index of the first unused byte in the given
DER element. |
| 96 |
| 97 Raises a ValueError exception in case of parsing errors. |
| 98 Raises an IndexError exception if the DER element is too short. |
| 99 """ |
| 100 try: |
| 101 self.typeTag = bord(derEle[0]) |
| 102 if (self.typeTag & 0x1F)==0x1F: |
| 103 raise ValueError("Unsupported DER tag") |
| 104 (length,idx) = self._decodeLen(1, derEle) |
| 105 if noLeftOvers and len(derEle) != (idx+length): |
| 106 raise ValueError("Not a DER structure") |
| 107 self.payload = derEle[idx:idx+length] |
| 108 except IndexError: |
| 109 raise ValueError("Not a valid DER SEQUENCE.") |
| 110 return idx+length |
| 111 |
| 112 class DerInteger(DerObject): |
| 113 def __init__(self, value = 0): |
| 114 """Class to model an INTEGER DER element. |
| 115 |
| 116 Limitation: only non-negative values are supported. |
| 117 """ |
| 118 DerObject.__init__(self, 'INTEGER') |
| 119 self.value = value |
| 120 |
| 121 def encode(self): |
| 122 """Return a complete INTEGER DER element, fully encoded as a TLV
.""" |
| 123 self.payload = long_to_bytes(self.value) |
| 124 if bord(self.payload[0])>127: |
| 125 self.payload = bchr(0x00) + self.payload |
| 126 return DerObject.encode(self) |
| 127 |
| 128 def decode(self, derEle, noLeftOvers=0): |
| 129 """Decode a complete INTEGER DER element, and re-initializes thi
s |
| 130 object with it. |
| 131 |
| 132 @param derEle A complete INTEGER DER element. It must star
t with a DER |
| 133 INTEGER tag. |
| 134 @param noLeftOvers Indicate whether it is acceptable to complet
e the |
| 135 parsing of the DER element and find that not
all |
| 136 bytes in derEle have been used. |
| 137 @return Index of the first unused byte in the given
DER element. |
| 138 |
| 139 Raises a ValueError exception if the DER element is not a |
| 140 valid non-negative INTEGER. |
| 141 Raises an IndexError exception if the DER element is too short. |
| 142 """ |
| 143 tlvLength = DerObject.decode(self, derEle, noLeftOvers) |
| 144 if self.typeTag!=self.typeTags['INTEGER']: |
| 145 raise ValueError ("Not a DER INTEGER.") |
| 146 if bord(self.payload[0])>127: |
| 147 raise ValueError ("Negative INTEGER.") |
| 148 self.value = bytes_to_long(self.payload) |
| 149 return tlvLength |
| 150 |
| 151 class DerSequence(DerObject): |
| 152 """Class to model a SEQUENCE DER element. |
| 153 |
| 154 This object behave like a dynamic Python sequence. |
| 155 Sub-elements that are INTEGERs, look like Python integers. |
| 156 Any other sub-element is a binary string encoded as the complete DER |
| 157 sub-element (TLV). |
| 158 """ |
| 159 |
| 160 def __init__(self, startSeq=None): |
| 161 """Initialize the SEQUENCE DER object. Always empty |
| 162 initially.""" |
| 163 DerObject.__init__(self, 'SEQUENCE') |
| 164 if startSeq==None: |
| 165 self._seq = [] |
| 166 else: |
| 167 self._seq = startSeq |
| 168 |
| 169 ## A few methods to make it behave like a python sequence |
| 170 |
| 171 def __delitem__(self, n): |
| 172 del self._seq[n] |
| 173 def __getitem__(self, n): |
| 174 return self._seq[n] |
| 175 def __setitem__(self, key, value): |
| 176 self._seq[key] = value |
| 177 def __setslice__(self,i,j,sequence): |
| 178 self._seq[i:j] = sequence |
| 179 def __delslice__(self,i,j): |
| 180 del self._seq[i:j] |
| 181 def __getslice__(self, i, j): |
| 182 return self._seq[max(0, i):max(0, j)] |
| 183 def __len__(self): |
| 184 return len(self._seq) |
| 185 def append(self, item): |
| 186 return self._seq.append(item) |
| 187 |
| 188 def hasInts(self): |
| 189 """Return the number of items in this sequence that are numbers.
""" |
| 190 return len(filter(isInt, self._seq)) |
| 191 |
| 192 def hasOnlyInts(self): |
| 193 """Return True if all items in this sequence are numbers.""" |
| 194 return self._seq and self.hasInts()==len(self._seq) |
| 195 |
| 196 def encode(self): |
| 197 """Return the DER encoding for the ASN.1 SEQUENCE, containing |
| 198 the non-negative integers and longs added to this object. |
| 199 |
| 200 Limitation: Raises a ValueError exception if it some elements |
| 201 in the sequence are neither Python integers nor complete DER INT
EGERs. |
| 202 """ |
| 203 self.payload = b('') |
| 204 for item in self._seq: |
| 205 try: |
| 206 self.payload += item |
| 207 except: |
| 208 try: |
| 209 self.payload += DerInteger(item).encode(
) |
| 210 except: |
| 211 raise ValueError("Trying to DER encode a
n unknown object") |
| 212 return DerObject.encode(self) |
| 213 |
| 214 def decode(self, derEle, noLeftOvers=0): |
| 215 """Decode a complete SEQUENCE DER element, and re-initializes th
is |
| 216 object with it. |
| 217 |
| 218 @param derEle A complete SEQUENCE DER element. It must sta
rt with a DER |
| 219 SEQUENCE tag. |
| 220 @param noLeftOvers Indicate whether it is acceptable to complet
e the |
| 221 parsing of the DER element and find that not
all |
| 222 bytes in derEle have been used. |
| 223 @return Index of the first unused byte in the given
DER element. |
| 224 |
| 225 DER INTEGERs are decoded into Python integers. Any other DER |
| 226 element is not decoded. Its validity is not checked. |
| 227 |
| 228 Raises a ValueError exception if the DER element is not a |
| 229 valid DER SEQUENCE. |
| 230 Raises an IndexError exception if the DER element is too short. |
| 231 """ |
| 232 |
| 233 self._seq = [] |
| 234 try: |
| 235 tlvLength = DerObject.decode(self, derEle, noLeftOvers) |
| 236 if self.typeTag!=self.typeTags['SEQUENCE']: |
| 237 raise ValueError("Not a DER SEQUENCE.") |
| 238 # Scan one TLV at once |
| 239 idx = 0 |
| 240 while idx<len(self.payload): |
| 241 typeTag = bord(self.payload[idx]) |
| 242 if typeTag==self.typeTags['INTEGER']: |
| 243 newInteger = DerInteger() |
| 244 idx += newInteger.decode(self.payload[id
x:]) |
| 245 self._seq.append(newInteger.value) |
| 246 else: |
| 247 itemLen,itemIdx = self._decodeLen(idx+1,
self.payload) |
| 248 self._seq.append(self.payload[idx:itemId
x+itemLen]) |
| 249 idx = itemIdx + itemLen |
| 250 except IndexError: |
| 251 raise ValueError("Not a valid DER SEQUENCE.") |
| 252 return tlvLength |
| 253 |
| 254 class DerOctetString(DerObject): |
| 255 def __init__(self, value = b('')): |
| 256 DerObject.__init__(self, 'OCTET STRING') |
| 257 self.payload = value |
| 258 |
| 259 def decode(self, derEle, noLeftOvers=0): |
| 260 p = DerObject.decode(derEle, noLeftOvers) |
| 261 if not self.isType("OCTET STRING"): |
| 262 raise ValueError("Not a valid OCTET STRING.") |
| 263 return p |
| 264 |
| 265 class DerNull(DerObject): |
| 266 def __init__(self): |
| 267 DerObject.__init__(self, 'NULL') |
| 268 |
| 269 class DerObjectId(DerObject): |
| 270 def __init__(self): |
| 271 DerObject.__init__(self, 'OBJECT IDENTIFIER') |
| 272 |
| 273 def decode(self, derEle, noLeftOvers=0): |
| 274 p = DerObject.decode(derEle, noLeftOvers) |
| 275 if not self.isType("OBJECT IDENTIFIER"): |
| 276 raise ValueError("Not a valid OBJECT IDENTIFIER.") |
| 277 return p |
| 278 |
| 279 def isInt(x): |
| 280 test = 0 |
| 281 try: |
| 282 test += x |
| 283 except TypeError: |
| 284 return 0 |
| 285 return 1 |
| 286 |
OLD | NEW |