diff --git a/src/googleupdateadmx/GoogleUpdateAdmx/GoogleUpdate.admx b/src/googleupdateadmx/GoogleUpdateAdmx/GoogleUpdate.admx deleted file mode 100644 index 2c149ff3..00000000 Binary files a/src/googleupdateadmx/GoogleUpdateAdmx/GoogleUpdate.admx and /dev/null differ diff --git a/src/googleupdateadmx/GoogleUpdateAdmx/en-US/GoogleUpdate.adml b/src/googleupdateadmx/GoogleUpdateAdmx/en-US/GoogleUpdate.adml deleted file mode 100644 index 96835758..00000000 Binary files a/src/googleupdateadmx/GoogleUpdateAdmx/en-US/GoogleUpdate.adml and /dev/null differ diff --git a/src/pyasn1/__init__.py b/src/pyasn1/__init__.py new file mode 100644 index 00000000..5f093000 --- /dev/null +++ b/src/pyasn1/__init__.py @@ -0,0 +1,8 @@ +import sys + +# http://www.python.org/dev/peps/pep-0396/ +__version__ = '0.1.9' + +if sys.version_info[:2] < (2, 4): + raise RuntimeError('PyASN1 requires Python 2.4 or later') + diff --git a/src/pyasn1/codec/__init__.py b/src/pyasn1/codec/__init__.py new file mode 100644 index 00000000..8c3066b2 --- /dev/null +++ b/src/pyasn1/codec/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/src/pyasn1/codec/ber/__init__.py b/src/pyasn1/codec/ber/__init__.py new file mode 100644 index 00000000..8c3066b2 --- /dev/null +++ b/src/pyasn1/codec/ber/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/src/pyasn1/codec/ber/decoder.py b/src/pyasn1/codec/ber/decoder.py new file mode 100644 index 00000000..61bfbce7 --- /dev/null +++ b/src/pyasn1/codec/ber/decoder.py @@ -0,0 +1,841 @@ +# BER decoder +from pyasn1.type import tag, univ, char, useful, tagmap +from pyasn1.codec.ber import eoo +from pyasn1.compat.octets import oct2int, isOctetsType +from pyasn1 import debug, error + +class AbstractDecoder: + protoComponent = None + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,)) + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,)) + +class AbstractSimpleDecoder(AbstractDecoder): + tagFormats = (tag.tagFormatSimple,) + def _createComponent(self, asn1Spec, tagSet, value=None): + if tagSet[0][1] not in self.tagFormats: + raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType())) + if asn1Spec is None: + return self.protoComponent.clone(value, tagSet) + elif value is None: + return asn1Spec + else: + return asn1Spec.clone(value) + +class AbstractConstructedDecoder(AbstractDecoder): + tagFormats = (tag.tagFormatConstructed,) + def _createComponent(self, asn1Spec, tagSet, value=None): + if tagSet[0][1] not in self.tagFormats: + raise error.PyAsn1Error('Invalid tag format %s for %s' % (tagSet[0], self.protoComponent.prettyPrintType())) + if asn1Spec is None: + return self.protoComponent.clone(tagSet) + else: + return asn1Spec.clone() + +class ExplicitTagDecoder(AbstractSimpleDecoder): + protoComponent = univ.Any('') + tagFormats = (tag.tagFormatConstructed,) + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + if substrateFun: + return substrateFun( + self._createComponent(asn1Spec, tagSet, ''), + substrate, length + ) + head, tail = substrate[:length], substrate[length:] + value, _ = decodeFun(head, asn1Spec, tagSet, length) + return value, tail + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + if substrateFun: + return substrateFun( + self._createComponent(asn1Spec, tagSet, ''), + substrate, length + ) + value, substrate = decodeFun(substrate, asn1Spec, tagSet, length) + terminator, substrate = decodeFun(substrate, allowEoo=True) + if eoo.endOfOctets.isSameTypeWith(terminator) and \ + terminator == eoo.endOfOctets: + return value, substrate + else: + raise error.PyAsn1Error('Missing end-of-octets terminator') + +explicitTagDecoder = ExplicitTagDecoder() + +class IntegerDecoder(AbstractSimpleDecoder): + protoComponent = univ.Integer(0) + precomputedValues = { + '\x00': 0, + '\x01': 1, + '\x02': 2, + '\x03': 3, + '\x04': 4, + '\x05': 5, + '\x06': 6, + '\x07': 7, + '\x08': 8, + '\x09': 9, + '\xff': -1, + '\xfe': -2, + '\xfd': -3, + '\xfc': -4, + '\xfb': -5 + } + + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length, + state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + if not head: + return self._createComponent(asn1Spec, tagSet, 0), tail + if head in self.precomputedValues: + value = self.precomputedValues[head] + else: + firstOctet = oct2int(head[0]) + if firstOctet & 0x80: + value = -1 + else: + value = 0 + for octet in head: + value = value << 8 | oct2int(octet) + return self._createComponent(asn1Spec, tagSet, value), tail + +class BooleanDecoder(IntegerDecoder): + protoComponent = univ.Boolean(0) + def _createComponent(self, asn1Spec, tagSet, value=None): + return IntegerDecoder._createComponent(self, asn1Spec, tagSet, value and 1 or 0) + +class BitStringDecoder(AbstractSimpleDecoder): + protoComponent = univ.BitString(()) + tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed) + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length, + state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check? + if not head: + raise error.PyAsn1Error('Empty substrate') + trailingBits = oct2int(head[0]) + if trailingBits > 7: + raise error.PyAsn1Error( + 'Trailing bits overflow %s' % trailingBits + ) + head = head[1:] + lsb = p = 0; l = len(head)-1; b = [] + while p <= l: + if p == l: + lsb = trailingBits + j = 7 + o = oct2int(head[p]) + while j >= lsb: + b.append((o>>j)&0x01) + j = j - 1 + p = p + 1 + return self._createComponent(asn1Spec, tagSet, b), tail + r = self._createComponent(asn1Spec, tagSet, ()) + if substrateFun: + return substrateFun(r, substrate, length) + while head: + component, head = decodeFun(head, self.protoComponent) + r = r + component + return r, tail + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + r = self._createComponent(asn1Spec, tagSet, '') + if substrateFun: + return substrateFun(r, substrate, length) + while substrate: + component, substrate = decodeFun(substrate, self.protoComponent, + allowEoo=True) + if eoo.endOfOctets.isSameTypeWith(component) and \ + component == eoo.endOfOctets: + break + r = r + component + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + return r, substrate + +class OctetStringDecoder(AbstractSimpleDecoder): + protoComponent = univ.OctetString('') + tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed) + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length, + state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + if tagSet[0][1] == tag.tagFormatSimple: # XXX what tag to check? + return self._createComponent(asn1Spec, tagSet, head), tail + r = self._createComponent(asn1Spec, tagSet, '') + if substrateFun: + return substrateFun(r, substrate, length) + while head: + component, head = decodeFun(head, self.protoComponent) + r = r + component + return r, tail + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + r = self._createComponent(asn1Spec, tagSet, '') + if substrateFun: + return substrateFun(r, substrate, length) + while substrate: + component, substrate = decodeFun(substrate, self.protoComponent, + allowEoo=True) + if eoo.endOfOctets.isSameTypeWith(component) and \ + component == eoo.endOfOctets: + break + r = r + component + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + return r, substrate + +class NullDecoder(AbstractSimpleDecoder): + protoComponent = univ.Null('') + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + r = self._createComponent(asn1Spec, tagSet) + if head: + raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length) + return r, tail + +class ObjectIdentifierDecoder(AbstractSimpleDecoder): + protoComponent = univ.ObjectIdentifier(()) + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length, + state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + if not head: + raise error.PyAsn1Error('Empty substrate') + + oid = () + index = 0 + substrateLen = len(head) + while index < substrateLen: + subId = oct2int(head[index]) + index += 1 + if subId < 128: + oid = oid + (subId,) + elif subId > 128: + # Construct subid from a number of octets + nextSubId = subId + subId = 0 + while nextSubId >= 128: + subId = (subId << 7) + (nextSubId & 0x7F) + if index >= substrateLen: + raise error.SubstrateUnderrunError( + 'Short substrate for sub-OID past %s' % (oid,) + ) + nextSubId = oct2int(head[index]) + index += 1 + oid = oid + ((subId << 7) + nextSubId,) + elif subId == 128: + # ASN.1 spec forbids leading zeros (0x80) in OID + # encoding, tolerating it opens a vulnerability. See + # http://www.cosic.esat.kuleuven.be/publications/article-1432.pdf + # page 7 + raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding') + + # Decode two leading arcs + if 0 <= oid[0] <= 39: + oid = (0,) + oid + elif 40 <= oid[0] <= 79: + oid = (1, oid[0]-40) + oid[1:] + elif oid[0] >= 80: + oid = (2, oid[0]-80) + oid[1:] + else: + raise error.PyAsn1Error('Malformed first OID octet: %s' % head[0]) + + return self._createComponent(asn1Spec, tagSet, oid), tail + +class RealDecoder(AbstractSimpleDecoder): + protoComponent = univ.Real() + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + if not head: + return self._createComponent(asn1Spec, tagSet, 0.0), tail + fo = oct2int(head[0]); head = head[1:] + if fo & 0x80: # binary encoding + if not head: + raise error.PyAsn1Error("Incomplete floating-point value") + n = (fo & 0x03) + 1 + if n == 4: + n = oct2int(head[0]) + head = head[1:] + eo, head = head[:n], head[n:] + if not eo or not head: + raise error.PyAsn1Error('Real exponent screwed') + e = oct2int(eo[0]) & 0x80 and -1 or 0 + while eo: # exponent + e <<= 8 + e |= oct2int(eo[0]) + eo = eo[1:] + b = fo >> 4 & 0x03 # base bits + if b > 2: + raise error.PyAsn1Error('Illegal Real base') + if b == 1: # encbase = 8 + e *= 3 + elif b == 2: # encbase = 16 + e *= 4 + p = 0 + while head: # value + p <<= 8 + p |= oct2int(head[0]) + head = head[1:] + if fo & 0x40: # sign bit + p = -p + sf = fo >> 2 & 0x03 # scale bits + p *= 2**sf + value = (p, 2, e) + elif fo & 0x40: # infinite value + value = fo & 0x01 and '-inf' or 'inf' + elif fo & 0xc0 == 0: # character encoding + if not head: + raise error.PyAsn1Error("Incomplete floating-point value") + try: + if fo & 0x3 == 0x1: # NR1 + value = (int(head), 10, 0) + elif fo & 0x3 == 0x2: # NR2 + value = float(head) + elif fo & 0x3 == 0x3: # NR3 + value = float(head) + else: + raise error.SubstrateUnderrunError( + 'Unknown NR (tag %s)' % fo + ) + except ValueError: + raise error.SubstrateUnderrunError( + 'Bad character Real syntax' + ) + else: + raise error.SubstrateUnderrunError( + 'Unknown encoding (tag %s)' % fo + ) + return self._createComponent(asn1Spec, tagSet, value), tail + +class SequenceDecoder(AbstractConstructedDecoder): + protoComponent = univ.Sequence() + def _getComponentTagMap(self, r, idx): + try: + return r.getComponentTagMapNearPosition(idx) + except error.PyAsn1Error: + return + + def _getComponentPositionByType(self, r, t, idx): + return r.getComponentPositionNearType(t, idx) + + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + r = self._createComponent(asn1Spec, tagSet) + idx = 0 + if substrateFun: + return substrateFun(r, substrate, length) + while head: + asn1Spec = self._getComponentTagMap(r, idx) + component, head = decodeFun(head, asn1Spec) + idx = self._getComponentPositionByType( + r, component.getEffectiveTagSet(), idx + ) + r.setComponentByPosition(idx, component, asn1Spec is None) + idx = idx + 1 + r.setDefaultComponents() + r.verifySizeSpec() + return r, tail + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + r = self._createComponent(asn1Spec, tagSet) + if substrateFun: + return substrateFun(r, substrate, length) + idx = 0 + while substrate: + asn1Spec = self._getComponentTagMap(r, idx) + component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True) + if eoo.endOfOctets.isSameTypeWith(component) and \ + component == eoo.endOfOctets: + break + idx = self._getComponentPositionByType( + r, component.getEffectiveTagSet(), idx + ) + r.setComponentByPosition(idx, component, asn1Spec is None) + idx = idx + 1 + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + r.setDefaultComponents() + r.verifySizeSpec() + return r, substrate + +class SequenceOfDecoder(AbstractConstructedDecoder): + protoComponent = univ.SequenceOf() + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + r = self._createComponent(asn1Spec, tagSet) + if substrateFun: + return substrateFun(r, substrate, length) + asn1Spec = r.getComponentType() + idx = 0 + while head: + component, head = decodeFun(head, asn1Spec) + r.setComponentByPosition(idx, component, asn1Spec is None) + idx = idx + 1 + r.verifySizeSpec() + return r, tail + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + r = self._createComponent(asn1Spec, tagSet) + if substrateFun: + return substrateFun(r, substrate, length) + asn1Spec = r.getComponentType() + idx = 0 + while substrate: + component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True) + if eoo.endOfOctets.isSameTypeWith(component) and \ + component == eoo.endOfOctets: + break + r.setComponentByPosition(idx, component, asn1Spec is None) + idx = idx + 1 + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + r.verifySizeSpec() + return r, substrate + +class SetDecoder(SequenceDecoder): + protoComponent = univ.Set() + def _getComponentTagMap(self, r, idx): + return r.getComponentTagMap() + + def _getComponentPositionByType(self, r, t, idx): + nextIdx = r.getComponentPositionByType(t) + if nextIdx is None: + return idx + else: + return nextIdx + +class SetOfDecoder(SequenceOfDecoder): + protoComponent = univ.SetOf() + +class ChoiceDecoder(AbstractConstructedDecoder): + protoComponent = univ.Choice() + tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed) + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + r = self._createComponent(asn1Spec, tagSet) + if substrateFun: + return substrateFun(r, substrate, length) + if r.getTagSet() == tagSet: # explicitly tagged Choice + component, head = decodeFun( + head, r.getComponentTagMap() + ) + else: + component, head = decodeFun( + head, r.getComponentTagMap(), tagSet, length, state + ) + if isinstance(component, univ.Choice): + effectiveTagSet = component.getEffectiveTagSet() + else: + effectiveTagSet = component.getTagSet() + r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None) + return r, tail + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + r = self._createComponent(asn1Spec, tagSet) + if substrateFun: + return substrateFun(r, substrate, length) + if r.getTagSet() == tagSet: # explicitly tagged Choice + component, substrate = decodeFun(substrate, r.getComponentTagMap()) + # eat up EOO marker + eooMarker, substrate = decodeFun(substrate, allowEoo=True) + if not eoo.endOfOctets.isSameTypeWith(eooMarker) or \ + eooMarker != eoo.endOfOctets: + raise error.PyAsn1Error('No EOO seen before substrate ends') + else: + component, substrate= decodeFun( + substrate, r.getComponentTagMap(), tagSet, length, state + ) + if isinstance(component, univ.Choice): + effectiveTagSet = component.getEffectiveTagSet() + else: + effectiveTagSet = component.getTagSet() + r.setComponentByType(effectiveTagSet, component, 0, asn1Spec is None) + return r, substrate + +class AnyDecoder(AbstractSimpleDecoder): + protoComponent = univ.Any() + tagFormats = (tag.tagFormatSimple, tag.tagFormatConstructed) + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + if asn1Spec is None or \ + asn1Spec is not None and tagSet != asn1Spec.getTagSet(): + # untagged Any container, recover inner header substrate + length = length + len(fullSubstrate) - len(substrate) + substrate = fullSubstrate + if substrateFun: + return substrateFun(self._createComponent(asn1Spec, tagSet), + substrate, length) + head, tail = substrate[:length], substrate[length:] + return self._createComponent(asn1Spec, tagSet, value=head), tail + + def indefLenValueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, + length, state, decodeFun, substrateFun): + if asn1Spec is not None and tagSet == asn1Spec.getTagSet(): + # tagged Any type -- consume header substrate + header = '' + else: + # untagged Any, recover header substrate + header = fullSubstrate[:-len(substrate)] + + r = self._createComponent(asn1Spec, tagSet, header) + + # Any components do not inherit initial tag + asn1Spec = self.protoComponent + + if substrateFun: + return substrateFun(r, substrate, length) + while substrate: + component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True) + if eoo.endOfOctets.isSameTypeWith(component) and \ + component == eoo.endOfOctets: + break + r = r + component + else: + raise error.SubstrateUnderrunError( + 'No EOO seen before substrate ends' + ) + return r, substrate + +# character string types +class UTF8StringDecoder(OctetStringDecoder): + protoComponent = char.UTF8String() +class NumericStringDecoder(OctetStringDecoder): + protoComponent = char.NumericString() +class PrintableStringDecoder(OctetStringDecoder): + protoComponent = char.PrintableString() +class TeletexStringDecoder(OctetStringDecoder): + protoComponent = char.TeletexString() +class VideotexStringDecoder(OctetStringDecoder): + protoComponent = char.VideotexString() +class IA5StringDecoder(OctetStringDecoder): + protoComponent = char.IA5String() +class GraphicStringDecoder(OctetStringDecoder): + protoComponent = char.GraphicString() +class VisibleStringDecoder(OctetStringDecoder): + protoComponent = char.VisibleString() +class GeneralStringDecoder(OctetStringDecoder): + protoComponent = char.GeneralString() +class UniversalStringDecoder(OctetStringDecoder): + protoComponent = char.UniversalString() +class BMPStringDecoder(OctetStringDecoder): + protoComponent = char.BMPString() + +# "useful" types +class ObjectDescriptorDecoder(OctetStringDecoder): + protoComponent = useful.ObjectDescriptor() +class GeneralizedTimeDecoder(OctetStringDecoder): + protoComponent = useful.GeneralizedTime() +class UTCTimeDecoder(OctetStringDecoder): + protoComponent = useful.UTCTime() + +tagMap = { + univ.Integer.tagSet: IntegerDecoder(), + univ.Boolean.tagSet: BooleanDecoder(), + univ.BitString.tagSet: BitStringDecoder(), + univ.OctetString.tagSet: OctetStringDecoder(), + univ.Null.tagSet: NullDecoder(), + univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(), + univ.Enumerated.tagSet: IntegerDecoder(), + univ.Real.tagSet: RealDecoder(), + univ.Sequence.tagSet: SequenceDecoder(), # conflicts with SequenceOf + univ.Set.tagSet: SetDecoder(), # conflicts with SetOf + univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any + # character string types + char.UTF8String.tagSet: UTF8StringDecoder(), + char.NumericString.tagSet: NumericStringDecoder(), + char.PrintableString.tagSet: PrintableStringDecoder(), + char.TeletexString.tagSet: TeletexStringDecoder(), + char.VideotexString.tagSet: VideotexStringDecoder(), + char.IA5String.tagSet: IA5StringDecoder(), + char.GraphicString.tagSet: GraphicStringDecoder(), + char.VisibleString.tagSet: VisibleStringDecoder(), + char.GeneralString.tagSet: GeneralStringDecoder(), + char.UniversalString.tagSet: UniversalStringDecoder(), + char.BMPString.tagSet: BMPStringDecoder(), + # useful types + useful.ObjectDescriptor.tagSet: ObjectDescriptorDecoder(), + useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(), + useful.UTCTime.tagSet: UTCTimeDecoder() +} + +# Type-to-codec map for ambiguous ASN.1 types +typeMap = { + univ.Set.typeId: SetDecoder(), + univ.SetOf.typeId: SetOfDecoder(), + univ.Sequence.typeId: SequenceDecoder(), + univ.SequenceOf.typeId: SequenceOfDecoder(), + univ.Choice.typeId: ChoiceDecoder(), + univ.Any.typeId: AnyDecoder() +} + +( stDecodeTag, stDecodeLength, stGetValueDecoder, stGetValueDecoderByAsn1Spec, + stGetValueDecoderByTag, stTryAsExplicitTag, stDecodeValue, + stDumpRawValue, stErrorCondition, stStop ) = [x for x in range(10)] + +class Decoder: + defaultErrorState = stErrorCondition +# defaultErrorState = stDumpRawValue + defaultRawDecoder = AnyDecoder() + supportIndefLength = True + def __init__(self, tagMap, typeMap={}): + self.__tagMap = tagMap + self.__typeMap = typeMap + # Tag & TagSet objects caches + self.__tagCache = {} + self.__tagSetCache = {} + + def __call__(self, substrate, asn1Spec=None, tagSet=None, + length=None, state=stDecodeTag, recursiveFlag=1, + substrateFun=None, allowEoo=False): + if debug.logger & debug.flagDecoder: + debug.logger('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate))) + fullSubstrate = substrate + while state != stStop: + if state == stDecodeTag: + if not substrate: + raise error.SubstrateUnderrunError( + 'Short octet stream on tag decoding' + ) + if not isOctetsType(substrate) and \ + not isinstance(substrate, univ.OctetString): + raise error.PyAsn1Error('Bad octet stream type') + # Decode tag + firstOctet = substrate[0] + substrate = substrate[1:] + if firstOctet in self.__tagCache: + lastTag = self.__tagCache[firstOctet] + else: + t = oct2int(firstOctet) + # Look for end-of-octets sentinel + if t == 0: + if substrate and oct2int(substrate[0]) == 0: + if allowEoo and self.supportIndefLength: + debug.logger and debug.logger & debug.flagDecoder and debug.logger('end-of-octets sentinel found') + value, substrate = eoo.endOfOctets, substrate[1:] + state = stStop + continue + else: + raise error.PyAsn1Error('Unexpected end-of-contents sentinel') + else: + raise error.PyAsn1Error('Zero tag encountered') + tagClass = t&0xC0 + tagFormat = t&0x20 + tagId = t&0x1F + if tagId == 0x1F: + tagId = 0 + while 1: + if not substrate: + raise error.SubstrateUnderrunError( + 'Short octet stream on long tag decoding' + ) + t = oct2int(substrate[0]) + tagId = tagId << 7 | (t&0x7F) + substrate = substrate[1:] + if not t&0x80: + break + lastTag = tag.Tag( + tagClass=tagClass, tagFormat=tagFormat, tagId=tagId + ) + if tagId < 31: + # cache short tags + self.__tagCache[firstOctet] = lastTag + if tagSet is None: + if firstOctet in self.__tagSetCache: + tagSet = self.__tagSetCache[firstOctet] + else: + # base tag not recovered + tagSet = tag.TagSet((), lastTag) + if firstOctet in self.__tagCache: + self.__tagSetCache[firstOctet] = tagSet + else: + tagSet = lastTag + tagSet + state = stDecodeLength + debug.logger and debug.logger & debug.flagDecoder and debug.logger('tag decoded into %s, decoding length' % tagSet) + if state == stDecodeLength: + # Decode length + if not substrate: + raise error.SubstrateUnderrunError( + 'Short octet stream on length decoding' + ) + firstOctet = oct2int(substrate[0]) + if firstOctet == 128: + size = 1 + length = -1 + elif firstOctet < 128: + length, size = firstOctet, 1 + else: + size = firstOctet & 0x7F + # encoded in size bytes + length = 0 + lengthString = substrate[1:size+1] + # missing check on maximum size, which shouldn't be a + # problem, we can handle more than is possible + if len(lengthString) != size: + raise error.SubstrateUnderrunError( + '%s<%s at %s' % + (size, len(lengthString), tagSet) + ) + for char in lengthString: + length = (length << 8) | oct2int(char) + size = size + 1 + substrate = substrate[size:] + if length != -1 and len(substrate) < length: + raise error.SubstrateUnderrunError( + '%d-octet short' % (length - len(substrate)) + ) + if length == -1 and not self.supportIndefLength: + error.PyAsn1Error('Indefinite length encoding not supported by this codec') + state = stGetValueDecoder + debug.logger and debug.logger & debug.flagDecoder and debug.logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length]))) + if state == stGetValueDecoder: + if asn1Spec is None: + state = stGetValueDecoderByTag + else: + state = stGetValueDecoderByAsn1Spec + # + # There're two ways of creating subtypes in ASN.1 what influences + # decoder operation. These methods are: + # 1) Either base types used in or no IMPLICIT tagging has been + # applied on subtyping. + # 2) Subtype syntax drops base type information (by means of + # IMPLICIT tagging. + # The first case allows for complete tag recovery from substrate + # while the second one requires original ASN.1 type spec for + # decoding. + # + # In either case a set of tags (tagSet) is coming from substrate + # in an incremental, tag-by-tag fashion (this is the case of + # EXPLICIT tag which is most basic). Outermost tag comes first + # from the wire. + # + if state == stGetValueDecoderByTag: + if tagSet in self.__tagMap: + concreteDecoder = self.__tagMap[tagSet] + else: + concreteDecoder = None + if concreteDecoder: + state = stDecodeValue + else: + _k = tagSet[:1] + if _k in self.__tagMap: + concreteDecoder = self.__tagMap[_k] + else: + concreteDecoder = None + if concreteDecoder: + state = stDecodeValue + else: + state = stTryAsExplicitTag + if debug.logger and debug.logger & debug.flagDecoder: + debug.logger('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "", state == stDecodeValue and 'value' or 'as explicit tag')) + debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__) + if state == stGetValueDecoderByAsn1Spec: + if isinstance(asn1Spec, (dict, tagmap.TagMap)): + if tagSet in asn1Spec: + __chosenSpec = asn1Spec[tagSet] + else: + __chosenSpec = None + if debug.logger and debug.logger & debug.flagDecoder: + debug.logger('candidate ASN.1 spec is a map of:') + for t, v in asn1Spec.getPosMap().items(): + debug.logger(' %s -> %s' % (t, v.__class__.__name__)) + if asn1Spec.getNegMap(): + debug.logger('but neither of: ') + for t, v in asn1Spec.getNegMap().items(): + debug.logger(' %s -> %s' % (t, v.__class__.__name__)) + debug.logger('new candidate ASN.1 spec is %s, chosen by %s' % (__chosenSpec is None and '' or __chosenSpec.prettyPrintType(), tagSet)) + else: + __chosenSpec = asn1Spec + debug.logger and debug.logger & debug.flagDecoder and debug.logger('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__) + if __chosenSpec is not None and ( + tagSet == __chosenSpec.getTagSet() or \ + tagSet in __chosenSpec.getTagMap() + ): + # use base type for codec lookup to recover untagged types + baseTagSet = __chosenSpec.baseTagSet + if __chosenSpec.typeId is not None and \ + __chosenSpec.typeId in self.__typeMap: + # ambiguous type + concreteDecoder = self.__typeMap[__chosenSpec.typeId] + debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen for an ambiguous type by type ID %s' % (__chosenSpec.typeId,)) + elif baseTagSet in self.__tagMap: + # base type or tagged subtype + concreteDecoder = self.__tagMap[baseTagSet] + debug.logger and debug.logger & debug.flagDecoder and debug.logger('value decoder chosen by base %s' % (baseTagSet,)) + else: + concreteDecoder = None + if concreteDecoder: + asn1Spec = __chosenSpec + state = stDecodeValue + else: + state = stTryAsExplicitTag + else: + concreteDecoder = None + state = stTryAsExplicitTag + if debug.logger and debug.logger & debug.flagDecoder: + debug.logger('codec %s chosen by ASN.1 spec, decoding %s' % (state == stDecodeValue and concreteDecoder.__class__.__name__ or "", state == stDecodeValue and 'value' or 'as explicit tag')) + debug.scope.push(__chosenSpec is None and '?' or __chosenSpec.__class__.__name__) + if state == stTryAsExplicitTag: + if tagSet and \ + tagSet[0][1] == tag.tagFormatConstructed and \ + tagSet[0][0] != tag.tagClassUniversal: + # Assume explicit tagging + concreteDecoder = explicitTagDecoder + state = stDecodeValue + else: + concreteDecoder = None + state = self.defaultErrorState + debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "", state == stDecodeValue and 'value' or 'as failure')) + if state == stDumpRawValue: + concreteDecoder = self.defaultRawDecoder + debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__) + state = stDecodeValue + if state == stDecodeValue: + if recursiveFlag == 0 and not substrateFun: # legacy + substrateFun = lambda a,b,c: (a,b[:c]) + if length == -1: # indef length + value, substrate = concreteDecoder.indefLenValueDecoder( + fullSubstrate, substrate, asn1Spec, tagSet, length, + stGetValueDecoder, self, substrateFun + ) + else: + value, substrate = concreteDecoder.valueDecoder( + fullSubstrate, substrate, asn1Spec, tagSet, length, + stGetValueDecoder, self, substrateFun + ) + state = stStop + debug.logger and debug.logger & debug.flagDecoder and debug.logger('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, value.prettyPrint(), substrate and debug.hexdump(substrate) or '')) + if state == stErrorCondition: + raise error.PyAsn1Error( + '%s not in asn1Spec: %s' % (tagSet, asn1Spec) + ) + if debug.logger and debug.logger & debug.flagDecoder: + debug.scope.pop() + debug.logger('decoder left scope %s, call completed' % debug.scope) + return value, substrate + +decode = Decoder(tagMap, typeMap) + +# XXX +# non-recursive decoding; return position rather than substrate diff --git a/src/pyasn1/codec/ber/encoder.py b/src/pyasn1/codec/ber/encoder.py new file mode 100644 index 00000000..0fb4ae71 --- /dev/null +++ b/src/pyasn1/codec/ber/encoder.py @@ -0,0 +1,433 @@ +# BER encoder +from pyasn1.type import base, tag, univ, char, useful +from pyasn1.codec.ber import eoo +from pyasn1.compat.octets import int2oct, oct2int, ints2octs, null, str2octs +from pyasn1 import debug, error + +class Error(Exception): pass + +class AbstractItemEncoder: + supportIndefLenMode = 1 + def encodeTag(self, t, isConstructed): + tagClass, tagFormat, tagId = t.asTuple() # this is a hotspot + v = tagClass | tagFormat + if isConstructed: + v = v|tag.tagFormatConstructed + if tagId < 31: + return int2oct(v|tagId) + else: + s = int2oct(tagId&0x7f) + tagId = tagId >> 7 + while tagId: + s = int2oct(0x80|(tagId&0x7f)) + s + tagId = tagId >> 7 + return int2oct(v|0x1F) + s + + def encodeLength(self, length, defMode): + if not defMode and self.supportIndefLenMode: + return int2oct(0x80) + if length < 0x80: + return int2oct(length) + else: + substrate = null + while length: + substrate = int2oct(length&0xff) + substrate + length = length >> 8 + substrateLen = len(substrate) + if substrateLen > 126: + raise Error('Length octets overflow (%d)' % substrateLen) + return int2oct(0x80 | substrateLen) + substrate + + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + raise Error('Not implemented') + + def _encodeEndOfOctets(self, encodeFun, defMode): + if defMode or not self.supportIndefLenMode: + return null + else: + return encodeFun(eoo.endOfOctets, defMode) + + def encode(self, encodeFun, value, defMode, maxChunkSize): + substrate, isConstructed = self.encodeValue( + encodeFun, value, defMode, maxChunkSize + ) + tagSet = value.getTagSet() + if tagSet: + if not isConstructed: # primitive form implies definite mode + defMode = 1 + return self.encodeTag( + tagSet[-1], isConstructed + ) + self.encodeLength( + len(substrate), defMode + ) + substrate + self._encodeEndOfOctets(encodeFun, defMode) + else: + return substrate # untagged value + +class EndOfOctetsEncoder(AbstractItemEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return null, 0 + +class ExplicitlyTaggedItemEncoder(AbstractItemEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + if isinstance(value, base.AbstractConstructedAsn1Item): + value = value.clone(tagSet=value.getTagSet()[:-1], + cloneValueFlag=1) + else: + value = value.clone(tagSet=value.getTagSet()[:-1]) + return encodeFun(value, defMode, maxChunkSize), 1 + +explicitlyTaggedItemEncoder = ExplicitlyTaggedItemEncoder() + +class BooleanEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + _true = ints2octs((1,)) + _false = ints2octs((0,)) + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return value and self._true or self._false, 0 + +class IntegerEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + supportCompactZero = False + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + if value == 0: # shortcut for zero value + if self.supportCompactZero: + # this seems to be a correct way for encoding zeros + return null, 0 + else: + # this seems to be a widespread way for encoding zeros + return ints2octs((0,)), 0 + octets = [] + value = int(value) # to save on ops on asn1 type + while 1: + octets.insert(0, value & 0xff) + if value == 0 or value == -1: + break + value = value >> 8 + if value == 0 and octets[0] & 0x80: + octets.insert(0, 0) + while len(octets) > 1 and \ + (octets[0] == 0 and octets[1] & 0x80 == 0 or \ + octets[0] == 0xff and octets[1] & 0x80 != 0): + del octets[0] + return ints2octs(octets), 0 + +class BitStringEncoder(AbstractItemEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + if not maxChunkSize or len(value) <= maxChunkSize*8: + out_len = (len(value) + 7) // 8 + out_list = out_len * [0] + j = 7 + i = -1 + for val in value: + j += 1 + if j == 8: + i += 1 + j = 0 + out_list[i] = out_list[i] | val << (7-j) + return int2oct(7-j) + ints2octs(out_list), 0 + else: + pos = 0; substrate = null + while 1: + # count in octets + v = value.clone(value[pos*8:pos*8+maxChunkSize*8]) + if not v: + break + substrate = substrate + encodeFun(v, defMode, maxChunkSize) + pos = pos + maxChunkSize + return substrate, 1 + +class OctetStringEncoder(AbstractItemEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + if not maxChunkSize or len(value) <= maxChunkSize: + return value.asOctets(), 0 + else: + pos = 0; substrate = null + while 1: + v = value.clone(value[pos:pos+maxChunkSize]) + if not v: + break + substrate = substrate + encodeFun(v, defMode, maxChunkSize) + pos = pos + maxChunkSize + return substrate, 1 + +class NullEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return null, 0 + +class ObjectIdentifierEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + precomputedValues = { + (1, 3, 6, 1, 2): (43, 6, 1, 2), + (1, 3, 6, 1, 4): (43, 6, 1, 4) + } + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + oid = value.asTuple() + if oid[:5] in self.precomputedValues: + octets = self.precomputedValues[oid[:5]] + oid = oid[5:] + else: + if len(oid) < 2: + raise error.PyAsn1Error('Short OID %s' % (value,)) + + octets = () + + # Build the first twos + if oid[0] == 0 and 0 <= oid[1] <= 39: + oid = (oid[1],) + oid[2:] + elif oid[0] == 1 and 0 <= oid[1] <= 39: + oid = (oid[1] + 40,) + oid[2:] + elif oid[0] == 2: + oid = (oid[1] + 80,) + oid[2:] + else: + raise error.PyAsn1Error( + 'Impossible initial arcs %s at %s' % (oid[:2], value) + ) + + # Cycle through subIds + for subId in oid: + if subId > -1 and subId < 128: + # Optimize for the common case + octets = octets + (subId & 0x7f,) + elif subId < 0: + raise error.PyAsn1Error( + 'Negative OID arc %s at %s' % (subId, value) + ) + else: + # Pack large Sub-Object IDs + res = (subId & 0x7f,) + subId = subId >> 7 + while subId > 0: + res = (0x80 | (subId & 0x7f),) + res + subId = subId >> 7 + # Add packed Sub-Object ID to resulted Object ID + octets += res + + return ints2octs(octets), 0 + +class RealEncoder(AbstractItemEncoder): + supportIndefLenMode = 0 + binEncBase = 2 # set to None to choose encoding base automatically + def _dropFloatingPoint(self, m, encbase, e): + ms, es = 1, 1 + if m < 0: + ms = -1 # mantissa sign + if e < 0: + es = -1 # exponenta sign + m *= ms + if encbase == 8: + m = m*2**(abs(e) % 3 * es) + e = abs(e) // 3 * es + elif encbase == 16: + m = m*2**(abs(e) % 4 * es) + e = abs(e) // 4 * es + + while 1: + if int(m) != m: + m *= encbase + e -= 1 + continue + break + return ms, int(m), encbase, e + + def _chooseEncBase(self, value): + m, b, e = value + base = [2, 8, 16] + if value.binEncBase in base: + return self._dropFloatingPoint(m, value.binEncBase, e) + elif self.binEncBase in base: + return self._dropFloatingPoint(m, self.binEncBase, e) + # auto choosing base 2/8/16 + mantissa = [m, m, m] + exponenta = [e, e, e] + encbase = 2 + e = float('inf') + for i in range(3): + sign, mantissa[i], base[i], exponenta[i] = \ + self._dropFloatingPoint(mantissa[i], base[i], exponenta[i]) + if abs(exponenta[i]) < abs(e) or \ + (abs(exponenta[i]) == abs(e) and mantissa[i] < m): + e = exponenta[i] + m = int(mantissa[i]) + encbase = base[i] + return sign, m, encbase, e + + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + if value.isPlusInfinity(): + return int2oct(0x40), 0 + if value.isMinusInfinity(): + return int2oct(0x41), 0 + m, b, e = value + if not m: + return null, 0 + if b == 10: + return str2octs('\x03%dE%s%d' % (m, e == 0 and '+' or '', e)), 0 + elif b == 2: + fo = 0x80 # binary encoding + ms, m, encbase, e = self._chooseEncBase(value) + if ms < 0: # mantissa sign + fo = fo | 0x40 # sign bit + # exponenta & mantissa normalization + if encbase == 2: + while m & 0x1 == 0: + m >>= 1 + e += 1 + elif encbase == 8: + while m & 0x7 == 0: + m >>= 3 + e += 1 + fo |= 0x10 + else: # encbase = 16 + while m & 0xf == 0: + m >>= 4 + e += 1 + fo |= 0x20 + sf = 0 # scale factor + while m & 0x1 == 0: + m >>= 1 + sf += 1 + if sf > 3: + raise error.PyAsn1Error('Scale factor overflow') # bug if raised + fo |= sf << 2 + eo = null + if e == 0 or e == -1: + eo = int2oct(e&0xff) + else: + while e not in (0, -1): + eo = int2oct(e&0xff) + eo + e >>= 8 + if e == 0 and eo and oct2int(eo[0]) & 0x80: + eo = int2oct(0) + eo + if e == -1 and eo and not (oct2int(eo[0]) & 0x80): + eo = int2oct(0xff) + eo + n = len(eo) + if n > 0xff: + raise error.PyAsn1Error('Real exponent overflow') + if n == 1: + pass + elif n == 2: + fo |= 1 + elif n == 3: + fo |= 2 + else: + fo |= 3 + eo = int2oct(n&0xff) + eo + po = null + while m: + po = int2oct(m&0xff) + po + m >>= 8 + substrate = int2oct(fo) + eo + po + return substrate, 0 + else: + raise error.PyAsn1Error('Prohibited Real base %s' % b) + +class SequenceEncoder(AbstractItemEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + value.setDefaultComponents() + value.verifySizeSpec() + substrate = null; idx = len(value) + while idx > 0: + idx = idx - 1 + if value[idx] is None: # Optional component + continue + component = value.getDefaultComponentByPosition(idx) + if component is not None and component == value[idx]: + continue + substrate = encodeFun( + value[idx], defMode, maxChunkSize + ) + substrate + return substrate, 1 + +class SequenceOfEncoder(AbstractItemEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + value.verifySizeSpec() + substrate = null; idx = len(value) + while idx > 0: + idx = idx - 1 + substrate = encodeFun( + value[idx], defMode, maxChunkSize + ) + substrate + return substrate, 1 + +class ChoiceEncoder(AbstractItemEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return encodeFun(value.getComponent(), defMode, maxChunkSize), 1 + +class AnyEncoder(OctetStringEncoder): + def encodeValue(self, encodeFun, value, defMode, maxChunkSize): + return value.asOctets(), defMode == 0 + +tagMap = { + eoo.endOfOctets.tagSet: EndOfOctetsEncoder(), + univ.Boolean.tagSet: BooleanEncoder(), + univ.Integer.tagSet: IntegerEncoder(), + univ.BitString.tagSet: BitStringEncoder(), + univ.OctetString.tagSet: OctetStringEncoder(), + univ.Null.tagSet: NullEncoder(), + univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(), + univ.Enumerated.tagSet: IntegerEncoder(), + univ.Real.tagSet: RealEncoder(), + # Sequence & Set have same tags as SequenceOf & SetOf + univ.SequenceOf.tagSet: SequenceOfEncoder(), + univ.SetOf.tagSet: SequenceOfEncoder(), + univ.Choice.tagSet: ChoiceEncoder(), + # character string types + char.UTF8String.tagSet: OctetStringEncoder(), + char.NumericString.tagSet: OctetStringEncoder(), + char.PrintableString.tagSet: OctetStringEncoder(), + char.TeletexString.tagSet: OctetStringEncoder(), + char.VideotexString.tagSet: OctetStringEncoder(), + char.IA5String.tagSet: OctetStringEncoder(), + char.GraphicString.tagSet: OctetStringEncoder(), + char.VisibleString.tagSet: OctetStringEncoder(), + char.GeneralString.tagSet: OctetStringEncoder(), + char.UniversalString.tagSet: OctetStringEncoder(), + char.BMPString.tagSet: OctetStringEncoder(), + # useful types + useful.ObjectDescriptor.tagSet: OctetStringEncoder(), + useful.GeneralizedTime.tagSet: OctetStringEncoder(), + useful.UTCTime.tagSet: OctetStringEncoder() + } + +# Type-to-codec map for ambiguous ASN.1 types +typeMap = { + univ.Set.typeId: SequenceEncoder(), + univ.SetOf.typeId: SequenceOfEncoder(), + univ.Sequence.typeId: SequenceEncoder(), + univ.SequenceOf.typeId: SequenceOfEncoder(), + univ.Choice.typeId: ChoiceEncoder(), + univ.Any.typeId: AnyEncoder() + } + +class Encoder: + supportIndefLength = True + def __init__(self, tagMap, typeMap={}): + self.__tagMap = tagMap + self.__typeMap = typeMap + + def __call__(self, value, defMode=True, maxChunkSize=0): + if not defMode and not self.supportIndefLength: + raise error.PyAsn1Error('Indefinite length encoding not supported by this codec') + debug.logger & debug.flagEncoder and debug.logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not defMode and 'in' or '', maxChunkSize, value.prettyPrintType(), value.prettyPrint())) + tagSet = value.getTagSet() + if len(tagSet) > 1: + concreteEncoder = explicitlyTaggedItemEncoder + else: + if value.typeId is not None and value.typeId in self.__typeMap: + concreteEncoder = self.__typeMap[value.typeId] + elif tagSet in self.__tagMap: + concreteEncoder = self.__tagMap[tagSet] + else: + tagSet = value.baseTagSet + if tagSet in self.__tagMap: + concreteEncoder = self.__tagMap[tagSet] + else: + raise Error('No encoder for %s' % (value,)) + debug.logger & debug.flagEncoder and debug.logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet)) + substrate = concreteEncoder.encode( + self, value, defMode, maxChunkSize + ) + debug.logger & debug.flagEncoder and debug.logger('built %s octets of substrate: %s\nencoder completed' % (len(substrate), debug.hexdump(substrate))) + return substrate + +encode = Encoder(tagMap, typeMap) diff --git a/src/pyasn1/codec/ber/eoo.py b/src/pyasn1/codec/ber/eoo.py new file mode 100644 index 00000000..379be199 --- /dev/null +++ b/src/pyasn1/codec/ber/eoo.py @@ -0,0 +1,8 @@ +from pyasn1.type import base, tag + +class EndOfOctets(base.AbstractSimpleAsn1Item): + defaultValue = 0 + tagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x00) + ) +endOfOctets = EndOfOctets() diff --git a/src/pyasn1/codec/cer/__init__.py b/src/pyasn1/codec/cer/__init__.py new file mode 100644 index 00000000..8c3066b2 --- /dev/null +++ b/src/pyasn1/codec/cer/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/src/pyasn1/codec/cer/decoder.py b/src/pyasn1/codec/cer/decoder.py new file mode 100644 index 00000000..1770cd87 --- /dev/null +++ b/src/pyasn1/codec/cer/decoder.py @@ -0,0 +1,35 @@ +# CER decoder +from pyasn1.type import univ +from pyasn1.codec.ber import decoder +from pyasn1.compat.octets import oct2int +from pyasn1 import error + +class BooleanDecoder(decoder.AbstractSimpleDecoder): + protoComponent = univ.Boolean(0) + def valueDecoder(self, fullSubstrate, substrate, asn1Spec, tagSet, length, + state, decodeFun, substrateFun): + head, tail = substrate[:length], substrate[length:] + if not head or length != 1: + raise error.PyAsn1Error('Not single-octet Boolean payload') + byte = oct2int(head[0]) + # CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while + # BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1 + # in http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + if byte == 0xff: + value = 1 + elif byte == 0x00: + value = 0 + else: + raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte) + return self._createComponent(asn1Spec, tagSet, value), tail + +tagMap = decoder.tagMap.copy() +tagMap.update({ + univ.Boolean.tagSet: BooleanDecoder() + }) + +typeMap = decoder.typeMap + +class Decoder(decoder.Decoder): pass + +decode = Decoder(tagMap, decoder.typeMap) diff --git a/src/pyasn1/codec/cer/encoder.py b/src/pyasn1/codec/cer/encoder.py new file mode 100644 index 00000000..61ce8a15 --- /dev/null +++ b/src/pyasn1/codec/cer/encoder.py @@ -0,0 +1,130 @@ +# CER encoder +from pyasn1.type import univ +from pyasn1.type import useful +from pyasn1.codec.ber import encoder +from pyasn1.compat.octets import int2oct, str2octs, null +from pyasn1 import error + +class BooleanEncoder(encoder.IntegerEncoder): + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + if client == 0: + substrate = int2oct(0) + else: + substrate = int2oct(255) + return substrate, 0 + +class BitStringEncoder(encoder.BitStringEncoder): + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + return encoder.BitStringEncoder.encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +class OctetStringEncoder(encoder.OctetStringEncoder): + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + return encoder.OctetStringEncoder.encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +class RealEncoder(encoder.RealEncoder): + def _chooseEncBase(self, value): + m, b, e = value + return self._dropFloatingPoint(m, b, e) + +# specialized GeneralStringEncoder here + +class GeneralizedTimeEncoder(OctetStringEncoder): + zchar = str2octs('Z') + pluschar = str2octs('+') + minuschar = str2octs('-') + zero = str2octs('0') + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + octets = client.asOctets() +# This breaks too many existing data items +# if '.' not in octets: +# raise error.PyAsn1Error('Format must include fraction of second: %r' % octets) + if len(octets) < 15: + raise error.PyAsn1Error('Bad UTC time length: %r' % octets) + if self.pluschar in octets or self.minuschar in octets: + raise error.PyAsn1Error('Must be UTC time: %r' % octets) + if octets[-1] != self.zchar[0]: + raise error.PyAsn1Error('Missing timezone specifier: %r' % octets) + return encoder.OctetStringEncoder.encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +class UTCTimeEncoder(encoder.OctetStringEncoder): + zchar = str2octs('Z') + pluschar = str2octs('+') + minuschar = str2octs('-') + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + octets = client.asOctets() + if self.pluschar in octets or self.minuschar in octets: + raise error.PyAsn1Error('Must be UTC time: %r' % octets) + if octets and octets[-1] != self.zchar[0]: + client = client.clone(octets + self.zchar) + if len(client) != 13: + raise error.PyAsn1Error('Bad UTC time length: %r' % client) + return encoder.OctetStringEncoder.encodeValue( + self, encodeFun, client, defMode, 1000 + ) + +class SetOfEncoder(encoder.SequenceOfEncoder): + def encodeValue(self, encodeFun, client, defMode, maxChunkSize): + if isinstance(client, univ.SequenceAndSetBase): + client.setDefaultComponents() + client.verifySizeSpec() + substrate = null; idx = len(client) + # This is certainly a hack but how else do I distinguish SetOf + # from Set if they have the same tags&constraints? + if isinstance(client, univ.SequenceAndSetBase): + # Set + comps = [] + while idx > 0: + idx = idx - 1 + if client[idx] is None: # Optional component + continue + if client.getDefaultComponentByPosition(idx) == client[idx]: + continue + comps.append(client[idx]) + comps.sort(key=lambda x: isinstance(x, univ.Choice) and \ + x.getMinTagSet() or x.getTagSet()) + for c in comps: + substrate += encodeFun(c, defMode, maxChunkSize) + else: + # SetOf + compSubs = [] + while idx > 0: + idx = idx - 1 + compSubs.append( + encodeFun(client[idx], defMode, maxChunkSize) + ) + compSubs.sort() # perhaps padding's not needed + substrate = null + for compSub in compSubs: + substrate += compSub + return substrate, 1 + +tagMap = encoder.tagMap.copy() +tagMap.update({ + univ.Boolean.tagSet: BooleanEncoder(), + univ.BitString.tagSet: BitStringEncoder(), + univ.OctetString.tagSet: OctetStringEncoder(), + univ.Real.tagSet: RealEncoder(), + useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(), + useful.UTCTime.tagSet: UTCTimeEncoder(), + univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set +}) + +typeMap = encoder.typeMap.copy() +typeMap.update({ + univ.Set.typeId: SetOfEncoder(), + univ.SetOf.typeId: SetOfEncoder() +}) + +class Encoder(encoder.Encoder): + def __call__(self, client, defMode=False, maxChunkSize=0): + return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) + +encode = Encoder(tagMap, typeMap) + +# EncoderFactory queries class instance and builds a map of tags -> encoders diff --git a/src/pyasn1/codec/der/__init__.py b/src/pyasn1/codec/der/__init__.py new file mode 100644 index 00000000..8c3066b2 --- /dev/null +++ b/src/pyasn1/codec/der/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/src/pyasn1/codec/der/decoder.py b/src/pyasn1/codec/der/decoder.py new file mode 100644 index 00000000..ea58d6d7 --- /dev/null +++ b/src/pyasn1/codec/der/decoder.py @@ -0,0 +1,9 @@ +# DER decoder +from pyasn1.codec.cer import decoder + +tagMap = decoder.tagMap +typeMap = decoder.typeMap +class Decoder(decoder.Decoder): + supportIndefLength = False + +decode = Decoder(tagMap, typeMap) diff --git a/src/pyasn1/codec/der/encoder.py b/src/pyasn1/codec/der/encoder.py new file mode 100644 index 00000000..7f55eeb9 --- /dev/null +++ b/src/pyasn1/codec/der/encoder.py @@ -0,0 +1,32 @@ +# DER encoder +from pyasn1.type import univ +from pyasn1.codec.cer import encoder +from pyasn1 import error + +class SetOfEncoder(encoder.SetOfEncoder): + def _cmpSetComponents(self, c1, c2): + tagSet1 = isinstance(c1, univ.Choice) and \ + c1.getEffectiveTagSet() or c1.getTagSet() + tagSet2 = isinstance(c2, univ.Choice) and \ + c2.getEffectiveTagSet() or c2.getTagSet() + return cmp(tagSet1, tagSet2) + +tagMap = encoder.tagMap.copy() +tagMap.update({ + # Overload CER encoders with BER ones (a bit hackerish XXX) + univ.BitString.tagSet: encoder.encoder.BitStringEncoder(), + univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(), + # Set & SetOf have same tags + univ.SetOf().tagSet: SetOfEncoder() +}) + +typeMap = encoder.typeMap + +class Encoder(encoder.Encoder): + supportIndefLength = False + def __call__(self, client, defMode=True, maxChunkSize=0): + if not defMode: + raise error.PyAsn1Error('DER forbids indefinite length mode') + return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) + +encode = Encoder(tagMap, typeMap) diff --git a/src/pyasn1/compat/__init__.py b/src/pyasn1/compat/__init__.py new file mode 100644 index 00000000..8c3066b2 --- /dev/null +++ b/src/pyasn1/compat/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/src/pyasn1/compat/binary.py b/src/pyasn1/compat/binary.py new file mode 100644 index 00000000..b38932af --- /dev/null +++ b/src/pyasn1/compat/binary.py @@ -0,0 +1,10 @@ +from sys import version_info + +if version_info[0:2] < (2, 6): + def bin(x): + if x <= 1: + return '0b'+str(x) + else: + return bin(x>>1) + str(x&1) +else: + bin = bin diff --git a/src/pyasn1/compat/octets.py b/src/pyasn1/compat/octets.py new file mode 100644 index 00000000..e8127370 --- /dev/null +++ b/src/pyasn1/compat/octets.py @@ -0,0 +1,22 @@ +from sys import version_info + +if version_info[0] <= 2: + int2oct = chr + ints2octs = lambda s: ''.join([ int2oct(x) for x in s ]) + null = '' + oct2int = ord + octs2ints = lambda s: [ oct2int(x) for x in s ] + str2octs = lambda x: x + octs2str = lambda x: x + isOctetsType = lambda s: isinstance(s, str) + isStringType = lambda s: isinstance(s, (str, unicode)) +else: + ints2octs = bytes + int2oct = lambda x: ints2octs((x,)) + null = ints2octs() + oct2int = lambda x: x + octs2ints = lambda s: [ x for x in s ] + str2octs = lambda x: x.encode() + octs2str = lambda x: x.decode() + isOctetsType = lambda s: isinstance(s, bytes) + isStringType = lambda s: isinstance(s, str) diff --git a/src/pyasn1/debug.py b/src/pyasn1/debug.py new file mode 100644 index 00000000..9b69886c --- /dev/null +++ b/src/pyasn1/debug.py @@ -0,0 +1,110 @@ +import time +import logging +from pyasn1.compat.octets import octs2ints +from pyasn1 import error +from pyasn1 import __version__ + +flagNone = 0x0000 +flagEncoder = 0x0001 +flagDecoder = 0x0002 +flagAll = 0xffff + +flagMap = { + 'encoder': flagEncoder, + 'decoder': flagDecoder, + 'all': flagAll + } + +class Printer: + def __init__(self, logger=None, handler=None, formatter=None): + if logger is None: + logger = logging.getLogger('pyasn1') + logger.setLevel(logging.DEBUG) + if handler is None: + handler = logging.StreamHandler() + if formatter is None: + formatter = logging.Formatter('%(asctime)s %(name)s: %(message)s') + handler.setFormatter(formatter) + handler.setLevel(logging.DEBUG) + logger.addHandler(handler) + self.__logger = logger + + def __call__(self, msg): self.__logger.debug(msg) + def __str__(self): return '' + +if hasattr(logging, 'NullHandler'): + NullHandler = logging.NullHandler +else: + # Python 2.6 and older + class NullHandler(logging.Handler): + def emit(self, record): + pass + +class Debug: + defaultPrinter = None + def __init__(self, *flags, **options): + self._flags = flagNone + if options.get('printer') is not None: + self._printer = options.get('printer') + elif self.defaultPrinter is not None: + self._printer = self.defaultPrinter + if 'loggerName' in options: + # route our logs to parent logger + self._printer = Printer( + logger=logging.getLogger(options['loggerName']), + handler=NullHandler() + ) + else: + self._printer = Printer() + self('running pyasn1 version %s' % __version__) + for f in flags: + inverse = f and f[0] in ('!', '~') + if inverse: + f = f[1:] + try: + if inverse: + self._flags &= ~flagMap[f] + else: + self._flags |= flagMap[f] + except KeyError: + raise error.PyAsn1Error('bad debug flag %s' % f) + + self('debug category \'%s\' %s' % (f, inverse and 'disabled' or 'enabled')) + + def __str__(self): + return 'logger %s, flags %x' % (self._printer, self._flags) + + def __call__(self, msg): + self._printer(msg) + + def __and__(self, flag): + return self._flags & flag + + def __rand__(self, flag): + return flag & self._flags + +logger = 0 + +def setLogger(l): + global logger + logger = l + +def hexdump(octets): + return ' '.join( + [ '%s%.2X' % (n%16 == 0 and ('\n%.5d: ' % n) or '', x) + for n,x in zip(range(len(octets)), octs2ints(octets)) ] + ) + +class Scope: + def __init__(self): + self._list = [] + + def __str__(self): return '.'.join(self._list) + + def push(self, token): + self._list.append(token) + + def pop(self): + return self._list.pop() + +scope = Scope() diff --git a/src/pyasn1/error.py b/src/pyasn1/error.py new file mode 100644 index 00000000..716406ff --- /dev/null +++ b/src/pyasn1/error.py @@ -0,0 +1,3 @@ +class PyAsn1Error(Exception): pass +class ValueConstraintError(PyAsn1Error): pass +class SubstrateUnderrunError(PyAsn1Error): pass diff --git a/src/pyasn1/type/__init__.py b/src/pyasn1/type/__init__.py new file mode 100644 index 00000000..8c3066b2 --- /dev/null +++ b/src/pyasn1/type/__init__.py @@ -0,0 +1 @@ +# This file is necessary to make this directory a package. diff --git a/src/pyasn1/type/base.py b/src/pyasn1/type/base.py new file mode 100644 index 00000000..72920a9d --- /dev/null +++ b/src/pyasn1/type/base.py @@ -0,0 +1,278 @@ +# Base classes for ASN.1 types +import sys +from pyasn1.type import constraint, tagmap, tag +from pyasn1 import error + +class Asn1Item: pass + +class Asn1ItemBase(Asn1Item): + # Set of tags for this ASN.1 type + tagSet = tag.TagSet() + + # A list of constraint.Constraint instances for checking values + subtypeSpec = constraint.ConstraintsIntersection() + + # Used for ambiguous ASN.1 types identification + typeId = None + + def __init__(self, tagSet=None, subtypeSpec=None): + if tagSet is None: + self._tagSet = self.tagSet + else: + self._tagSet = tagSet + if subtypeSpec is None: + self._subtypeSpec = self.subtypeSpec + else: + self._subtypeSpec = subtypeSpec + + def _verifySubtypeSpec(self, value, idx=None): + try: + self._subtypeSpec(value, idx) + except error.PyAsn1Error: + c, i, t = sys.exc_info() + raise c('%s at %s' % (i, self.__class__.__name__)) + + def getSubtypeSpec(self): return self._subtypeSpec + + def getTagSet(self): return self._tagSet + def getEffectiveTagSet(self): return self._tagSet # used by untagged types + def getTagMap(self): return tagmap.TagMap({self._tagSet: self}) + + def isSameTypeWith(self, other, matchTags=True, matchConstraints=True): + return self is other or \ + (not matchTags or \ + self._tagSet == other.getTagSet()) and \ + (not matchConstraints or \ + self._subtypeSpec==other.getSubtypeSpec()) + + def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True): + """Returns true if argument is a ASN1 subtype of ourselves""" + return (not matchTags or \ + self._tagSet.isSuperTagSetOf(other.getTagSet())) and \ + (not matchConstraints or \ + (self._subtypeSpec.isSuperTypeOf(other.getSubtypeSpec()))) + +class NoValue: + def __getattr__(self, attr): + raise error.PyAsn1Error('No value for %s()' % attr) + def __getitem__(self, i): + raise error.PyAsn1Error('No value') + def __repr__(self): return '%s()' % self.__class__.__name__ + +noValue = NoValue() + +# Base class for "simple" ASN.1 objects. These are immutable. +class AbstractSimpleAsn1Item(Asn1ItemBase): + defaultValue = noValue + def __init__(self, value=None, tagSet=None, subtypeSpec=None): + Asn1ItemBase.__init__(self, tagSet, subtypeSpec) + if value is None or value is noValue: + value = self.defaultValue + if value is None or value is noValue: + self.__hashedValue = value = noValue + else: + value = self.prettyIn(value) + self._verifySubtypeSpec(value) + self.__hashedValue = hash(value) + self._value = value + self._len = None + + def __repr__(self): + r = [] + if self._value is not self.defaultValue: + r.append(self.prettyOut(self._value)) + if self._tagSet is not self.tagSet: + r.append('tagSet=%r' % (self._tagSet,)) + if self._subtypeSpec is not self.subtypeSpec: + r.append('subtypeSpec=%r' % (self._subtypeSpec,)) + return '%s(%s)' % (self.__class__.__name__, ', '.join(r)) + + def __str__(self): return str(self._value) + def __eq__(self, other): + return self is other and True or self._value == other + def __ne__(self, other): return self._value != other + def __lt__(self, other): return self._value < other + def __le__(self, other): return self._value <= other + def __gt__(self, other): return self._value > other + def __ge__(self, other): return self._value >= other + if sys.version_info[0] <= 2: + def __nonzero__(self): return bool(self._value) + else: + def __bool__(self): return bool(self._value) + def __hash__(self): + return self.__hashedValue is noValue and hash(noValue) or self.__hashedValue + + def hasValue(self): + return not isinstance(self._value, NoValue) + + def clone(self, value=None, tagSet=None, subtypeSpec=None): + if value is None and tagSet is None and subtypeSpec is None: + return self + if value is None: + value = self._value + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + return self.__class__(value, tagSet, subtypeSpec) + + def subtype(self, value=None, implicitTag=None, explicitTag=None, + subtypeSpec=None): + if value is None: + value = self._value + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + return self.__class__(value, tagSet, subtypeSpec) + + def prettyIn(self, value): return value + def prettyOut(self, value): return str(value) + + def prettyPrint(self, scope=0): + if self.hasValue(): + return self.prettyOut(self._value) + else: + return '' + + # XXX Compatibility stub + def prettyPrinter(self, scope=0): return self.prettyPrint(scope) + + def prettyPrintType(self, scope=0): + return '%s -> %s' % (self.getTagSet(), self.__class__.__name__) + +# +# Constructed types: +# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice +# * ASN1 types and values are represened by Python class instances +# * Value initialization is made for defaulted components only +# * Primary method of component addressing is by-position. Data model for base +# type is Python sequence. Additional type-specific addressing methods +# may be implemented for particular types. +# * SequenceOf and SetOf types do not implement any additional methods +# * Sequence, Set and Choice types also implement by-identifier addressing +# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing +# * Sequence and Set types may include optional and defaulted +# components +# * Constructed types hold a reference to component types used for value +# verification and ordering. +# * Component type is a scalar type for SequenceOf/SetOf types and a list +# of types for Sequence/Set/Choice. +# + +class AbstractConstructedAsn1Item(Asn1ItemBase): + componentType = None + sizeSpec = constraint.ConstraintsIntersection() + def __init__(self, componentType=None, tagSet=None, + subtypeSpec=None, sizeSpec=None): + Asn1ItemBase.__init__(self, tagSet, subtypeSpec) + if componentType is None: + self._componentType = self.componentType + else: + self._componentType = componentType + if sizeSpec is None: + self._sizeSpec = self.sizeSpec + else: + self._sizeSpec = sizeSpec + self._componentValues = [] + self._componentValuesSet = 0 + + def __repr__(self): + r = [] + if self._componentType is not self.componentType: + r.append('componentType=%r' % (self._componentType,)) + if self._tagSet is not self.tagSet: + r.append('tagSet=%r' % (self._tagSet,)) + if self._subtypeSpec is not self.subtypeSpec: + r.append('subtypeSpec=%r' % (self._subtypeSpec,)) + r = '%s(%s)' % (self.__class__.__name__, ', '.join(r)) + if self._componentValues: + r += '.setComponents(%s)' % ', '.join([repr(x) for x in self._componentValues]) + return r + + def __eq__(self, other): + return self is other and True or self._componentValues == other + def __ne__(self, other): return self._componentValues != other + def __lt__(self, other): return self._componentValues < other + def __le__(self, other): return self._componentValues <= other + def __gt__(self, other): return self._componentValues > other + def __ge__(self, other): return self._componentValues >= other + if sys.version_info[0] <= 2: + def __nonzero__(self): return bool(self._componentValues) + else: + def __bool__(self): return bool(self._componentValues) + + def getComponentTagMap(self): + raise error.PyAsn1Error('Method not implemented') + + def _cloneComponentValues(self, myClone, cloneValueFlag): pass + + def clone(self, tagSet=None, subtypeSpec=None, sizeSpec=None, + cloneValueFlag=None): + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + if sizeSpec is None: + sizeSpec = self._sizeSpec + r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec) + if cloneValueFlag: + self._cloneComponentValues(r, cloneValueFlag) + return r + + def subtype(self, implicitTag=None, explicitTag=None, subtypeSpec=None, + sizeSpec=None, cloneValueFlag=None): + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + if sizeSpec is None: + sizeSpec = self._sizeSpec + else: + sizeSpec = sizeSpec + self._sizeSpec + r = self.__class__(self._componentType, tagSet, subtypeSpec, sizeSpec) + if cloneValueFlag: + self._cloneComponentValues(r, cloneValueFlag) + return r + + def _verifyComponent(self, idx, value): pass + + def verifySizeSpec(self): self._sizeSpec(self) + + def getComponentByPosition(self, idx): + raise error.PyAsn1Error('Method not implemented') + def setComponentByPosition(self, idx, value, verifyConstraints=True): + raise error.PyAsn1Error('Method not implemented') + + def setComponents(self, *args, **kwargs): + for idx in range(len(args)): + self[idx] = args[idx] + for k in kwargs: + self[k] = kwargs[k] + return self + + def getComponentType(self): return self._componentType + + def setDefaultComponents(self): pass + + def __getitem__(self, idx): return self.getComponentByPosition(idx) + def __setitem__(self, idx, value): self.setComponentByPosition(idx, value) + + def __len__(self): return len(self._componentValues) + + def clear(self): + self._componentValues = [] + self._componentValuesSet = 0 + diff --git a/src/pyasn1/type/char.py b/src/pyasn1/type/char.py new file mode 100644 index 00000000..af49ab3e --- /dev/null +++ b/src/pyasn1/type/char.py @@ -0,0 +1,64 @@ +# ASN.1 "character string" types +from pyasn1.type import univ, tag + +class NumericString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18) + ) + +class PrintableString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19) + ) + +class TeletexString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20) + ) + +class T61String(TeletexString): pass + +class VideotexString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21) + ) + +class IA5String(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22) + ) + +class GraphicString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25) + ) + +class VisibleString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26) + ) + +class ISO646String(VisibleString): pass + +class GeneralString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27) + ) + +class UniversalString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28) + ) + encoding = "utf-32-be" + +class BMPString(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30) + ) + encoding = "utf-16-be" + +class UTF8String(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12) + ) + encoding = "utf-8" diff --git a/src/pyasn1/type/constraint.py b/src/pyasn1/type/constraint.py new file mode 100644 index 00000000..66873937 --- /dev/null +++ b/src/pyasn1/type/constraint.py @@ -0,0 +1,200 @@ +# +# ASN.1 subtype constraints classes. +# +# Constraints are relatively rare, but every ASN1 object +# is doing checks all the time for whether they have any +# constraints and whether they are applicable to the object. +# +# What we're going to do is define objects/functions that +# can be called unconditionally if they are present, and that +# are simply not present if there are no constraints. +# +# Original concept and code by Mike C. Fletcher. +# +import sys +from pyasn1.type import error + +class AbstractConstraint: + """Abstract base-class for constraint objects + + Constraints should be stored in a simple sequence in the + namespace of their client Asn1Item sub-classes. + """ + def __init__(self, *values): + self._valueMap = {} + self._setValues(values) + self.__hashedValues = None + def __call__(self, value, idx=None): + try: + self._testValue(value, idx) + except error.ValueConstraintError: + raise error.ValueConstraintError( + '%s failed at: \"%s\"' % (self, sys.exc_info()[1]) + ) + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + ', '.join([repr(x) for x in self._values]) + ) + def __eq__(self, other): + return self is other and True or self._values == other + def __ne__(self, other): return self._values != other + def __lt__(self, other): return self._values < other + def __le__(self, other): return self._values <= other + def __gt__(self, other): return self._values > other + def __ge__(self, other): return self._values >= other + if sys.version_info[0] <= 2: + def __nonzero__(self): return bool(self._values) + else: + def __bool__(self): return bool(self._values) + + def __hash__(self): + if self.__hashedValues is None: + self.__hashedValues = hash((self.__class__.__name__, self._values)) + return self.__hashedValues + + def _setValues(self, values): self._values = values + def _testValue(self, value, idx): + raise error.ValueConstraintError(value) + + # Constraints derivation logic + def getValueMap(self): return self._valueMap + def isSuperTypeOf(self, otherConstraint): + return self in otherConstraint.getValueMap() or \ + otherConstraint is self or otherConstraint == self + def isSubTypeOf(self, otherConstraint): + return otherConstraint in self._valueMap or \ + otherConstraint is self or otherConstraint == self + +class SingleValueConstraint(AbstractConstraint): + """Value must be part of defined values constraint""" + def _testValue(self, value, idx): + # XXX index vals for performance? + if value not in self._values: + raise error.ValueConstraintError(value) + +class ContainedSubtypeConstraint(AbstractConstraint): + """Value must satisfy all of defined set of constraints""" + def _testValue(self, value, idx): + for c in self._values: + c(value, idx) + +class ValueRangeConstraint(AbstractConstraint): + """Value must be within start and stop values (inclusive)""" + def _testValue(self, value, idx): + if value < self.start or value > self.stop: + raise error.ValueConstraintError(value) + + def _setValues(self, values): + if len(values) != 2: + raise error.PyAsn1Error( + '%s: bad constraint values' % (self.__class__.__name__,) + ) + self.start, self.stop = values + if self.start > self.stop: + raise error.PyAsn1Error( + '%s: screwed constraint values (start > stop): %s > %s' % ( + self.__class__.__name__, + self.start, self.stop + ) + ) + AbstractConstraint._setValues(self, values) + +class ValueSizeConstraint(ValueRangeConstraint): + """len(value) must be within start and stop values (inclusive)""" + def _testValue(self, value, idx): + l = len(value) + if l < self.start or l > self.stop: + raise error.ValueConstraintError(value) + +class PermittedAlphabetConstraint(SingleValueConstraint): + def _setValues(self, values): + self._values = () + for v in values: + self._values = self._values + tuple(v) + + def _testValue(self, value, idx): + for v in value: + if v not in self._values: + raise error.ValueConstraintError(value) + +# This is a bit kludgy, meaning two op modes within a single constraing +class InnerTypeConstraint(AbstractConstraint): + """Value must satisfy type and presense constraints""" + def _testValue(self, value, idx): + if self.__singleTypeConstraint: + self.__singleTypeConstraint(value) + elif self.__multipleTypeConstraint: + if idx not in self.__multipleTypeConstraint: + raise error.ValueConstraintError(value) + constraint, status = self.__multipleTypeConstraint[idx] + if status == 'ABSENT': # XXX presense is not checked! + raise error.ValueConstraintError(value) + constraint(value) + + def _setValues(self, values): + self.__multipleTypeConstraint = {} + self.__singleTypeConstraint = None + for v in values: + if isinstance(v, tuple): + self.__multipleTypeConstraint[v[0]] = v[1], v[2] + else: + self.__singleTypeConstraint = v + AbstractConstraint._setValues(self, values) + +# Boolean ops on constraints + +class ConstraintsExclusion(AbstractConstraint): + """Value must not fit the single constraint""" + def _testValue(self, value, idx): + try: + self._values[0](value, idx) + except error.ValueConstraintError: + return + else: + raise error.ValueConstraintError(value) + + def _setValues(self, values): + if len(values) != 1: + raise error.PyAsn1Error('Single constraint expected') + AbstractConstraint._setValues(self, values) + +class AbstractConstraintSet(AbstractConstraint): + """Value must not satisfy the single constraint""" + def __getitem__(self, idx): return self._values[idx] + + def __add__(self, value): return self.__class__(self, value) + def __radd__(self, value): return self.__class__(self, value) + + def __len__(self): return len(self._values) + + # Constraints inclusion in sets + + def _setValues(self, values): + self._values = values + for v in values: + self._valueMap[v] = 1 + self._valueMap.update(v.getValueMap()) + +class ConstraintsIntersection(AbstractConstraintSet): + """Value must satisfy all constraints""" + def _testValue(self, value, idx): + for v in self._values: + v(value, idx) + +class ConstraintsUnion(AbstractConstraintSet): + """Value must satisfy at least one constraint""" + def _testValue(self, value, idx): + for v in self._values: + try: + v(value, idx) + except error.ValueConstraintError: + pass + else: + return + raise error.ValueConstraintError( + 'all of %s failed for \"%s\"' % (self._values, value) + ) + +# XXX +# add tests for type check diff --git a/src/pyasn1/type/error.py b/src/pyasn1/type/error.py new file mode 100644 index 00000000..3e684844 --- /dev/null +++ b/src/pyasn1/type/error.py @@ -0,0 +1,3 @@ +from pyasn1.error import PyAsn1Error + +class ValueConstraintError(PyAsn1Error): pass diff --git a/src/pyasn1/type/namedtype.py b/src/pyasn1/type/namedtype.py new file mode 100644 index 00000000..aca42828 --- /dev/null +++ b/src/pyasn1/type/namedtype.py @@ -0,0 +1,149 @@ +# NamedType specification for constructed types +import sys +from pyasn1.type import tagmap +from pyasn1 import error + +class NamedType: + isOptional = 0 + isDefaulted = 0 + def __init__(self, name, t): + self.__name = name; self.__type = t + def __repr__(self): return '%s(%r, %r)' % ( + self.__class__.__name__, self.__name, self.__type + ) + def __eq__(self, other): return tuple(self) == tuple(other) + def __ne__(self, other): return tuple(self) != tuple(other) + def __lt__(self, other): return tuple(self) < tuple(other) + def __le__(self, other): return tuple(self) <= tuple(other) + def __gt__(self, other): return tuple(self) > tuple(other) + def __ge__(self, other): return tuple(self) >= tuple(other) + def __hash__(self): return hash(tuple(self)) + + def getType(self): return self.__type + def getName(self): return self.__name + def __getitem__(self, idx): + if idx == 0: return self.__name + if idx == 1: return self.__type + raise IndexError() + +class OptionalNamedType(NamedType): + isOptional = 1 +class DefaultedNamedType(NamedType): + isDefaulted = 1 + +class NamedTypes: + def __init__(self, *namedTypes): + self.__namedTypes = namedTypes + self.__namedTypesLen = len(self.__namedTypes) + self.__minTagSet = None + self.__tagToPosIdx = {}; self.__nameToPosIdx = {} + self.__tagMap = { False: None, True: None } + self.__ambigiousTypes = {} + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + ', '.join([ repr(x) for x in self.__namedTypes ]) + ) + def __eq__(self, other): return tuple(self) == tuple(other) + def __ne__(self, other): return tuple(self) != tuple(other) + def __lt__(self, other): return tuple(self) < tuple(other) + def __le__(self, other): return tuple(self) <= tuple(other) + def __gt__(self, other): return tuple(self) > tuple(other) + def __ge__(self, other): return tuple(self) >= tuple(other) + def __hash__(self): return hash(tuple(self)) + + def __getitem__(self, idx): return self.__namedTypes[idx] + + if sys.version_info[0] <= 2: + def __nonzero__(self): return bool(self.__namedTypesLen) + else: + def __bool__(self): return bool(self.__namedTypesLen) + def __len__(self): return self.__namedTypesLen + + def clone(self): return self.__class__(*self.__namedTypes) + + def getTypeByPosition(self, idx): + if idx < 0 or idx >= self.__namedTypesLen: + raise error.PyAsn1Error('Type position out of range') + else: + return self.__namedTypes[idx].getType() + + def getPositionByType(self, tagSet): + if not self.__tagToPosIdx: + idx = self.__namedTypesLen + while idx > 0: + idx = idx - 1 + tagMap = self.__namedTypes[idx].getType().getTagMap() + for t in tagMap.getPosMap(): + if t in self.__tagToPosIdx: + raise error.PyAsn1Error('Duplicate type %s' % (t,)) + self.__tagToPosIdx[t] = idx + try: + return self.__tagToPosIdx[tagSet] + except KeyError: + raise error.PyAsn1Error('Type %s not found' % (tagSet,)) + + def getNameByPosition(self, idx): + try: + return self.__namedTypes[idx].getName() + except IndexError: + raise error.PyAsn1Error('Type position out of range') + def getPositionByName(self, name): + if not self.__nameToPosIdx: + idx = self.__namedTypesLen + while idx > 0: + idx = idx - 1 + n = self.__namedTypes[idx].getName() + if n in self.__nameToPosIdx: + raise error.PyAsn1Error('Duplicate name %s' % (n,)) + self.__nameToPosIdx[n] = idx + try: + return self.__nameToPosIdx[name] + except KeyError: + raise error.PyAsn1Error('Name %s not found' % (name,)) + + def __buildAmbigiousTagMap(self): + ambigiousTypes = () + idx = self.__namedTypesLen + while idx > 0: + idx = idx - 1 + t = self.__namedTypes[idx] + if t.isOptional or t.isDefaulted: + ambigiousTypes = (t, ) + ambigiousTypes + else: + ambigiousTypes = (t, ) + self.__ambigiousTypes[idx] = NamedTypes(*ambigiousTypes) + + def getTagMapNearPosition(self, idx): + if not self.__ambigiousTypes: self.__buildAmbigiousTagMap() + try: + return self.__ambigiousTypes[idx].getTagMap() + except KeyError: + raise error.PyAsn1Error('Type position out of range') + + def getPositionNearType(self, tagSet, idx): + if not self.__ambigiousTypes: self.__buildAmbigiousTagMap() + try: + return idx+self.__ambigiousTypes[idx].getPositionByType(tagSet) + except KeyError: + raise error.PyAsn1Error('Type position out of range') + + def genMinTagSet(self): + if self.__minTagSet is None: + for t in self.__namedTypes: + __type = t.getType() + tagSet = getattr(__type,'getMinTagSet',__type.getTagSet)() + if self.__minTagSet is None or tagSet < self.__minTagSet: + self.__minTagSet = tagSet + return self.__minTagSet + + def getTagMap(self, uniq=False): + if self.__tagMap[uniq] is None: + tagMap = tagmap.TagMap() + for nt in self.__namedTypes: + tagMap = tagMap.clone( + nt.getType(), nt.getType().getTagMap(), uniq + ) + self.__tagMap[uniq] = tagMap + return self.__tagMap[uniq] diff --git a/src/pyasn1/type/namedval.py b/src/pyasn1/type/namedval.py new file mode 100644 index 00000000..676cb934 --- /dev/null +++ b/src/pyasn1/type/namedval.py @@ -0,0 +1,58 @@ +# ASN.1 named integers +from pyasn1 import error + +__all__ = [ 'NamedValues' ] + +class NamedValues: + def __init__(self, *namedValues): + self.nameToValIdx = {}; self.valToNameIdx = {} + self.namedValues = () + automaticVal = 1 + for namedValue in namedValues: + if isinstance(namedValue, tuple): + name, val = namedValue + else: + name = namedValue + val = automaticVal + if name in self.nameToValIdx: + raise error.PyAsn1Error('Duplicate name %s' % (name,)) + self.nameToValIdx[name] = val + if val in self.valToNameIdx: + raise error.PyAsn1Error('Duplicate value %s=%s' % (name, val)) + self.valToNameIdx[val] = name + self.namedValues = self.namedValues + ((name, val),) + automaticVal = automaticVal + 1 + + def __repr__(self): + return '%s(%s)' % (self.__class__.__name__, ', '.join([repr(x) for x in self.namedValues])) + + def __str__(self): return str(self.namedValues) + + def __eq__(self, other): return tuple(self) == tuple(other) + def __ne__(self, other): return tuple(self) != tuple(other) + def __lt__(self, other): return tuple(self) < tuple(other) + def __le__(self, other): return tuple(self) <= tuple(other) + def __gt__(self, other): return tuple(self) > tuple(other) + def __ge__(self, other): return tuple(self) >= tuple(other) + def __hash__(self): return hash(tuple(self)) + + def getName(self, value): + if value in self.valToNameIdx: + return self.valToNameIdx[value] + + def getValue(self, name): + if name in self.nameToValIdx: + return self.nameToValIdx[name] + + def __getitem__(self, i): return self.namedValues[i] + def __len__(self): return len(self.namedValues) + + def __add__(self, namedValues): + return self.__class__(*self.namedValues + namedValues) + def __radd__(self, namedValues): + return self.__class__(*namedValues + tuple(self)) + + def clone(self, *namedValues): + return self.__class__(*tuple(self) + namedValues) + +# XXX clone/subtype? diff --git a/src/pyasn1/type/tag.py b/src/pyasn1/type/tag.py new file mode 100644 index 00000000..7471a9b1 --- /dev/null +++ b/src/pyasn1/type/tag.py @@ -0,0 +1,128 @@ +# ASN.1 types tags +from operator import getitem +from pyasn1 import error + +tagClassUniversal = 0x00 +tagClassApplication = 0x40 +tagClassContext = 0x80 +tagClassPrivate = 0xC0 + +tagFormatSimple = 0x00 +tagFormatConstructed = 0x20 + +tagCategoryImplicit = 0x01 +tagCategoryExplicit = 0x02 +tagCategoryUntagged = 0x04 + +class Tag: + def __init__(self, tagClass, tagFormat, tagId): + if tagId < 0: + raise error.PyAsn1Error( + 'Negative tag ID (%s) not allowed' % (tagId,) + ) + self.__tag = (tagClass, tagFormat, tagId) + self.uniq = (tagClass, tagId) + self.__hashedUniqTag = hash(self.uniq) + + def __str__(self): + return '[%s:%s:%s]' % self.__tag + + def __repr__(self): + return '%s(tagClass=%s, tagFormat=%s, tagId=%s)' % ( + (self.__class__.__name__,) + self.__tag + ) + # These is really a hotspot -- expose public "uniq" attribute to save on + # function calls + def __eq__(self, other): return self.uniq == other.uniq + def __ne__(self, other): return self.uniq != other.uniq + def __lt__(self, other): return self.uniq < other.uniq + def __le__(self, other): return self.uniq <= other.uniq + def __gt__(self, other): return self.uniq > other.uniq + def __ge__(self, other): return self.uniq >= other.uniq + def __hash__(self): return self.__hashedUniqTag + def __getitem__(self, idx): return self.__tag[idx] + def __and__(self, otherTag): + (tagClass, tagFormat, tagId) = otherTag + return self.__class__( + self.__tag&tagClass, self.__tag&tagFormat, self.__tag&tagId + ) + def __or__(self, otherTag): + (tagClass, tagFormat, tagId) = otherTag + return self.__class__( + self.__tag[0]|tagClass, + self.__tag[1]|tagFormat, + self.__tag[2]|tagId + ) + def asTuple(self): return self.__tag # __getitem__() is slow + +class TagSet: + def __init__(self, baseTag=(), *superTags): + self.__baseTag = baseTag + self.__superTags = superTags + self.__hashedSuperTags = hash(superTags) + _uniq = () + for t in superTags: + _uniq = _uniq + t.uniq + self.uniq = _uniq + self.__lenOfSuperTags = len(superTags) + + def __str__(self): + return self.__superTags and '+'.join([str(x) for x in self.__superTags]) or '[untagged]' + + def __repr__(self): + return '%s(%s)' % ( + self.__class__.__name__, + '(), ' + ', '.join([repr(x) for x in self.__superTags]) + ) + + def __add__(self, superTag): + return self.__class__( + self.__baseTag, *self.__superTags + (superTag,) + ) + def __radd__(self, superTag): + return self.__class__( + self.__baseTag, *(superTag,) + self.__superTags + ) + + def tagExplicitly(self, superTag): + tagClass, tagFormat, tagId = superTag + if tagClass == tagClassUniversal: + raise error.PyAsn1Error( + 'Can\'t tag with UNIVERSAL-class tag' + ) + if tagFormat != tagFormatConstructed: + superTag = Tag(tagClass, tagFormatConstructed, tagId) + return self + superTag + + def tagImplicitly(self, superTag): + tagClass, tagFormat, tagId = superTag + if self.__superTags: + superTag = Tag(tagClass, self.__superTags[-1][1], tagId) + return self[:-1] + superTag + + def getBaseTag(self): return self.__baseTag + def __getitem__(self, idx): + if isinstance(idx, slice): + return self.__class__( + self.__baseTag, *getitem(self.__superTags, idx) + ) + return self.__superTags[idx] + def __eq__(self, other): return self.uniq == other.uniq + def __ne__(self, other): return self.uniq != other.uniq + def __lt__(self, other): return self.uniq < other.uniq + def __le__(self, other): return self.uniq <= other.uniq + def __gt__(self, other): return self.uniq > other.uniq + def __ge__(self, other): return self.uniq >= other.uniq + def __hash__(self): return self.__hashedSuperTags + def __len__(self): return self.__lenOfSuperTags + def isSuperTagSetOf(self, tagSet): + if len(tagSet) < self.__lenOfSuperTags: + return + idx = self.__lenOfSuperTags - 1 + while idx >= 0: + if self.__superTags[idx] != tagSet[idx]: + return + idx = idx - 1 + return 1 + +def initTagSet(tag): return TagSet(tag, tag) diff --git a/src/pyasn1/type/tagmap.py b/src/pyasn1/type/tagmap.py new file mode 100644 index 00000000..feb91ae3 --- /dev/null +++ b/src/pyasn1/type/tagmap.py @@ -0,0 +1,66 @@ +from pyasn1 import error + +class TagMap: + def __init__(self, posMap={}, negMap={}, defType=None): + self.__posMap = posMap.copy() + self.__negMap = negMap.copy() + self.__defType = defType + + def __contains__(self, tagSet): + return tagSet in self.__posMap or \ + self.__defType is not None and tagSet not in self.__negMap + + def __getitem__(self, tagSet): + if tagSet in self.__posMap: + return self.__posMap[tagSet] + elif tagSet in self.__negMap: + raise error.PyAsn1Error('Key in negative map') + elif self.__defType is not None: + return self.__defType + else: + raise KeyError() + + def __repr__(self): + s = self.__class__.__name__ + '(' + if self.__posMap: + s = s + 'posMap=%r, ' % (self.__posMap,) + if self.__negMap: + s = s + 'negMap=%r, ' % (self.__negMap,) + if self.__defType is not None: + s = s + 'defType=%r' % (self.__defType,) + return s + ')' + + def __str__(self): + s = self.__class__.__name__ + ':\n' + if self.__posMap: + s = s + 'posMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__posMap.values()]) + if self.__negMap: + s = s + 'negMap:\n%s, ' % ',\n '.join([ x.prettyPrintType() for x in self.__negMap.values()]) + if self.__defType is not None: + s = s + 'defType:\n%s, ' % self.__defType.prettyPrintType() + return s + + def clone(self, parentType, tagMap, uniq=False): + if self.__defType is not None and tagMap.getDef() is not None: + raise error.PyAsn1Error('Duplicate default value at %s' % (self,)) + if tagMap.getDef() is not None: + defType = tagMap.getDef() + else: + defType = self.__defType + + posMap = self.__posMap.copy() + for k in tagMap.getPosMap(): + if uniq and k in posMap: + raise error.PyAsn1Error('Duplicate positive key %s' % (k,)) + posMap[k] = parentType + + negMap = self.__negMap.copy() + negMap.update(tagMap.getNegMap()) + + return self.__class__( + posMap, negMap, defType, + ) + + def getPosMap(self): return self.__posMap.copy() + def getNegMap(self): return self.__negMap.copy() + def getDef(self): return self.__defType diff --git a/src/pyasn1/type/univ.py b/src/pyasn1/type/univ.py new file mode 100644 index 00000000..4ed640f2 --- /dev/null +++ b/src/pyasn1/type/univ.py @@ -0,0 +1,1156 @@ +# ASN.1 "universal" data types +import operator, sys, math +from pyasn1.type import base, tag, constraint, namedtype, namedval, tagmap +from pyasn1.codec.ber import eoo +from pyasn1.compat import octets +from pyasn1 import error + +# "Simple" ASN.1 types (yet incomplete) + +class Integer(base.AbstractSimpleAsn1Item): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x02) + ) + namedValues = namedval.NamedValues() + def __init__(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if namedValues is None: + self.__namedValues = self.namedValues + else: + self.__namedValues = namedValues + base.AbstractSimpleAsn1Item.__init__( + self, value, tagSet, subtypeSpec + ) + + def __repr__(self): + if self.__namedValues is not self.namedValues: + return '%s, %r)' % (base.AbstractSimpleAsn1Item.__repr__(self)[:-1], self.__namedValues) + else: + return base.AbstractSimpleAsn1Item.__repr__(self) + + def __and__(self, value): return self.clone(self._value & value) + def __rand__(self, value): return self.clone(value & self._value) + def __or__(self, value): return self.clone(self._value | value) + def __ror__(self, value): return self.clone(value | self._value) + def __xor__(self, value): return self.clone(self._value ^ value) + def __rxor__(self, value): return self.clone(value ^ self._value) + def __lshift__(self, value): return self.clone(self._value << value) + def __rshift__(self, value): return self.clone(self._value >> value) + + def __add__(self, value): return self.clone(self._value + value) + def __radd__(self, value): return self.clone(value + self._value) + def __sub__(self, value): return self.clone(self._value - value) + def __rsub__(self, value): return self.clone(value - self._value) + def __mul__(self, value): return self.clone(self._value * value) + def __rmul__(self, value): return self.clone(value * self._value) + def __mod__(self, value): return self.clone(self._value % value) + def __rmod__(self, value): return self.clone(value % self._value) + def __pow__(self, value, modulo=None): return self.clone(pow(self._value, value, modulo)) + def __rpow__(self, value): return self.clone(pow(value, self._value)) + + if sys.version_info[0] <= 2: + def __div__(self, value): return self.clone(self._value // value) + def __rdiv__(self, value): return self.clone(value // self._value) + else: + def __truediv__(self, value): return self.clone(self._value / value) + def __rtruediv__(self, value): return self.clone(value / self._value) + def __divmod__(self, value): return self.clone(self._value // value) + def __rdivmod__(self, value): return self.clone(value // self._value) + + __hash__ = base.AbstractSimpleAsn1Item.__hash__ + + def __int__(self): return int(self._value) + if sys.version_info[0] <= 2: + def __long__(self): return long(self._value) + def __float__(self): return float(self._value) + def __abs__(self): return self.clone(abs(self._value)) + def __index__(self): return int(self._value) + def __pos__(self): return self.clone(+self._value) + def __neg__(self): return self.clone(-self._value) + def __invert__(self): return self.clone(~self._value) + def __round__(self, n=0): + r = round(self._value, n) + if n: + return self.clone(r) + else: + return r + def __floor__(self): return math.floor(self._value) + def __ceil__(self): return math.ceil(self._value) + if sys.version_info[0:2] > (2, 5): + def __trunc__(self): return self.clone(math.trunc(self._value)) + + def __lt__(self, value): return self._value < value + def __le__(self, value): return self._value <= value + def __eq__(self, value): return self._value == value + def __ne__(self, value): return self._value != value + def __gt__(self, value): return self._value > value + def __ge__(self, value): return self._value >= value + + def prettyIn(self, value): + if not isinstance(value, str): + try: + return int(value) + except: + raise error.PyAsn1Error( + 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1]) + ) + r = self.__namedValues.getValue(value) + if r is not None: + return r + try: + return int(value) + except: + raise error.PyAsn1Error( + 'Can\'t coerce %r into integer: %s' % (value, sys.exc_info()[1]) + ) + + def prettyOut(self, value): + r = self.__namedValues.getName(value) + return r is None and str(value) or repr(r) + + def getNamedValues(self): return self.__namedValues + + def clone(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if value is None and tagSet is None and subtypeSpec is None \ + and namedValues is None: + return self + if value is None: + value = self._value + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + if namedValues is None: + namedValues = self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + + def subtype(self, value=None, implicitTag=None, explicitTag=None, + subtypeSpec=None, namedValues=None): + if value is None: + value = self._value + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + if namedValues is None: + namedValues = self.__namedValues + else: + namedValues = namedValues + self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + +class Boolean(Integer): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x01), + ) + subtypeSpec = Integer.subtypeSpec+constraint.SingleValueConstraint(0,1) + namedValues = Integer.namedValues.clone(('False', 0), ('True', 1)) + +class BitString(base.AbstractSimpleAsn1Item): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x03) + ) + namedValues = namedval.NamedValues() + def __init__(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if namedValues is None: + self.__namedValues = self.namedValues + else: + self.__namedValues = namedValues + base.AbstractSimpleAsn1Item.__init__( + self, value, tagSet, subtypeSpec + ) + + def clone(self, value=None, tagSet=None, subtypeSpec=None, + namedValues=None): + if value is None and tagSet is None and subtypeSpec is None \ + and namedValues is None: + return self + if value is None: + value = self._value + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + if namedValues is None: + namedValues = self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + + def subtype(self, value=None, implicitTag=None, explicitTag=None, + subtypeSpec=None, namedValues=None): + if value is None: + value = self._value + if implicitTag is not None: + tagSet = self._tagSet.tagImplicitly(implicitTag) + elif explicitTag is not None: + tagSet = self._tagSet.tagExplicitly(explicitTag) + else: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + else: + subtypeSpec = subtypeSpec + self._subtypeSpec + if namedValues is None: + namedValues = self.__namedValues + else: + namedValues = namedValues + self.__namedValues + return self.__class__(value, tagSet, subtypeSpec, namedValues) + + def __str__(self): return str(tuple(self)) + + # Immutable sequence object protocol + + def __len__(self): + if self._len is None: + self._len = len(self._value) + return self._len + def __getitem__(self, i): + if isinstance(i, slice): + return self.clone(operator.getitem(self._value, i)) + else: + return self._value[i] + + def __add__(self, value): return self.clone(self._value + value) + def __radd__(self, value): return self.clone(value + self._value) + def __mul__(self, value): return self.clone(self._value * value) + def __rmul__(self, value): return self * value + + def prettyIn(self, value): + r = [] + if not value: + return () + elif isinstance(value, str): + if value[0] == '\'': + if value[-2:] == '\'B': + for v in value[1:-2]: + if v == '0': + r.append(0) + elif v == '1': + r.append(1) + else: + raise error.PyAsn1Error( + 'Non-binary BIT STRING initializer %s' % (v,) + ) + return tuple(r) + elif value[-2:] == '\'H': + for v in value[1:-2]: + i = 4 + v = int(v, 16) + while i: + i = i - 1 + r.append((v>>i)&0x01) + return tuple(r) + else: + raise error.PyAsn1Error( + 'Bad BIT STRING value notation %s' % (value,) + ) + else: + for i in value.split(','): + j = self.__namedValues.getValue(i) + if j is None: + raise error.PyAsn1Error( + 'Unknown bit identifier \'%s\'' % (i,) + ) + if j >= len(r): + r.extend([0]*(j-len(r)+1)) + r[j] = 1 + return tuple(r) + elif isinstance(value, (tuple, list)): + r = tuple(value) + for b in r: + if b and b != 1: + raise error.PyAsn1Error( + 'Non-binary BitString initializer \'%s\'' % (r,) + ) + return r + elif isinstance(value, BitString): + return tuple(value) + else: + raise error.PyAsn1Error( + 'Bad BitString initializer type \'%s\'' % (value,) + ) + + def prettyOut(self, value): + return '\"\'%s\'B\"' % ''.join([str(x) for x in value]) + +try: + all +except NameError: # Python 2.4 + def all(iterable): + for element in iterable: + if not element: + return False + return True + +class OctetString(base.AbstractSimpleAsn1Item): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x04) + ) + defaultBinValue = defaultHexValue = base.noValue + encoding = 'us-ascii' + def __init__(self, value=None, tagSet=None, subtypeSpec=None, + encoding=None, binValue=None, hexValue=None): + if encoding is None: + self._encoding = self.encoding + else: + self._encoding = encoding + if binValue is not None: + value = self.fromBinaryString(binValue) + if hexValue is not None: + value = self.fromHexString(hexValue) + if value is None or value is base.noValue: + value = self.defaultHexValue + if value is None or value is base.noValue: + value = self.defaultBinValue + self.__asNumbersCache = None + base.AbstractSimpleAsn1Item.__init__(self, value, tagSet, subtypeSpec) + + def clone(self, value=None, tagSet=None, subtypeSpec=None, + encoding=None, binValue=None, hexValue=None): + if value is None and tagSet is None and subtypeSpec is None and \ + encoding is None and binValue is None and hexValue is None: + return self + if value is None and binValue is None and hexValue is None: + value = self._value + if tagSet is None: + tagSet = self._tagSet + if subtypeSpec is None: + subtypeSpec = self._subtypeSpec + if encoding is None: + encoding = self._encoding + return self.__class__( + value, tagSet, subtypeSpec, encoding, binValue, hexValue + ) + + if sys.version_info[0] <= 2: + def prettyIn(self, value): + if isinstance(value, str): + return value + elif isinstance(value, unicode): + try: + return value.encode(self._encoding) + except (LookupError, UnicodeEncodeError): + raise error.PyAsn1Error( + 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding) + ) + elif isinstance(value, (tuple, list)): + try: + return ''.join([ chr(x) for x in value ]) + except ValueError: + raise error.PyAsn1Error( + 'Bad OctetString initializer \'%s\'' % (value,) + ) + else: + return str(value) + else: + def prettyIn(self, value): + if isinstance(value, bytes): + return value + elif isinstance(value, str): + try: + return value.encode(self._encoding) + except UnicodeEncodeError: + raise error.PyAsn1Error( + 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding) + ) + elif isinstance(value, OctetString): + return value.asOctets() + elif isinstance(value, (tuple, list, map)): + try: + return bytes(value) + except ValueError: + raise error.PyAsn1Error( + 'Bad OctetString initializer \'%s\'' % (value,) + ) + else: + try: + return str(value).encode(self._encoding) + except UnicodeEncodeError: + raise error.PyAsn1Error( + 'Can\'t encode string \'%s\' with \'%s\' codec' % (value, self._encoding) + ) + + + def fromBinaryString(self, value): + bitNo = 8; byte = 0; r = () + for v in value: + if bitNo: + bitNo = bitNo - 1 + else: + bitNo = 7 + r = r + (byte,) + byte = 0 + if v == '0': + v = 0 + elif v == '1': + v = 1 + else: + raise error.PyAsn1Error( + 'Non-binary OCTET STRING initializer %s' % (v,) + ) + byte = byte | (v << bitNo) + return octets.ints2octs(r + (byte,)) + + def fromHexString(self, value): + r = p = () + for v in value: + if p: + r = r + (int(p+v, 16),) + p = () + else: + p = v + if p: + r = r + (int(p+'0', 16),) + return octets.ints2octs(r) + + def prettyOut(self, value): + if sys.version_info[0] <= 2: + numbers = tuple(( ord(x) for x in value )) + else: + numbers = tuple(value) + if all(x >= 32 and x <= 126 for x in numbers): + return str(value) + else: + return '0x' + ''.join(( '%.2x' % x for x in numbers )) + + def __repr__(self): + r = [] + doHex = False + if self._value is not self.defaultValue: + for x in self.asNumbers(): + if x < 32 or x > 126: + doHex = True + break + if not doHex: + r.append('%r' % (self._value,)) + if self._tagSet is not self.tagSet: + r.append('tagSet=%r' % (self._tagSet,)) + if self._subtypeSpec is not self.subtypeSpec: + r.append('subtypeSpec=%r' % (self._subtypeSpec,)) + if self.encoding is not self._encoding: + r.append('encoding=%r' % (self._encoding,)) + if doHex: + r.append('hexValue=%r' % ''.join([ '%.2x' % x for x in self.asNumbers() ])) + return '%s(%s)' % (self.__class__.__name__, ', '.join(r)) + + if sys.version_info[0] <= 2: + def __str__(self): return str(self._value) + def __unicode__(self): + return self._value.decode(self._encoding, 'ignore') + def asOctets(self): return self._value + def asNumbers(self): + if self.__asNumbersCache is None: + self.__asNumbersCache = tuple([ ord(x) for x in self._value ]) + return self.__asNumbersCache + else: + def __str__(self): return self._value.decode(self._encoding, 'ignore') + def __bytes__(self): return self._value + def asOctets(self): return self._value + def asNumbers(self): + if self.__asNumbersCache is None: + self.__asNumbersCache = tuple(self._value) + return self.__asNumbersCache + + # Immutable sequence object protocol + + def __len__(self): + if self._len is None: + self._len = len(self._value) + return self._len + def __getitem__(self, i): + if isinstance(i, slice): + return self.clone(operator.getitem(self._value, i)) + else: + return self._value[i] + + def __add__(self, value): return self.clone(self._value + self.prettyIn(value)) + def __radd__(self, value): return self.clone(self.prettyIn(value) + self._value) + def __mul__(self, value): return self.clone(self._value * value) + def __rmul__(self, value): return self * value + def __int__(self): return int(self._value) + def __float__(self): return float(self._value) + +class Null(OctetString): + defaultValue = ''.encode() # This is tightly constrained + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x05) + ) + subtypeSpec = OctetString.subtypeSpec+constraint.SingleValueConstraint(''.encode()) + +if sys.version_info[0] <= 2: + intTypes = (int, long) +else: + intTypes = (int,) + +numericTypes = intTypes + (float,) + +class ObjectIdentifier(base.AbstractSimpleAsn1Item): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x06) + ) + def __add__(self, other): return self.clone(self._value + other) + def __radd__(self, other): return self.clone(other + self._value) + + def asTuple(self): return self._value + + # Sequence object protocol + + def __len__(self): + if self._len is None: + self._len = len(self._value) + return self._len + def __getitem__(self, i): + if isinstance(i, slice): + return self.clone( + operator.getitem(self._value, i) + ) + else: + return self._value[i] + + def __str__(self): return self.prettyPrint() + def __repr__(self): + return '%s(%r)' % (self.__class__.__name__, self.prettyPrint()) + + def index(self, suboid): return self._value.index(suboid) + + def isPrefixOf(self, value): + """Returns true if argument OID resides deeper in the OID tree""" + l = len(self) + if l <= len(value): + if self._value[:l] == value[:l]: + return 1 + return 0 + + def prettyIn(self, value): + """Dotted -> tuple of numerics OID converter""" + if isinstance(value, tuple): + pass + elif isinstance(value, ObjectIdentifier): + return tuple(value) + elif octets.isStringType(value): + r = [] + for element in [ x for x in value.split('.') if x != '' ]: + try: + r.append(int(element, 0)) + except ValueError: + raise error.PyAsn1Error( + 'Malformed Object ID %s at %s: %s' % + (str(value), self.__class__.__name__, sys.exc_info()[1]) + ) + value = tuple(r) + else: + try: + value = tuple(value) + except TypeError: + raise error.PyAsn1Error( + 'Malformed Object ID %s at %s: %s' % + (str(value), self.__class__.__name__,sys.exc_info()[1]) + ) + + for x in value: + if not isinstance(x, intTypes) or x < 0: + raise error.PyAsn1Error( + 'Invalid sub-ID in %s at %s' % (value, self.__class__.__name__) + ) + + return value + + def prettyOut(self, value): return '.'.join([ str(x) for x in value ]) + +class Real(base.AbstractSimpleAsn1Item): + binEncBase = None # binEncBase = 16 is recommended for large numbers + try: + _plusInf = float('inf') + _minusInf = float('-inf') + _inf = (_plusInf, _minusInf) + except ValueError: + # Infinity support is platform and Python dependent + _plusInf = _minusInf = None + _inf = () + + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x09) + ) + + def __normalizeBase10(self, value): + m, b, e = value + while m and m % 10 == 0: + m = m / 10 + e = e + 1 + return m, b, e + + def prettyIn(self, value): + if isinstance(value, tuple) and len(value) == 3: + if not isinstance(value[0], numericTypes) or \ + not isinstance(value[1], intTypes) or \ + not isinstance(value[2], intTypes): + raise error.PyAsn1Error('Lame Real value syntax: %s' % (value,)) + if isinstance(value[0], float) and \ + self._inf and value[0] in self._inf: + return value[0] + if value[1] not in (2, 10): + raise error.PyAsn1Error( + 'Prohibited base for Real value: %s' % (value[1],) + ) + if value[1] == 10: + value = self.__normalizeBase10(value) + return value + elif isinstance(value, intTypes): + return self.__normalizeBase10((value, 10, 0)) + elif isinstance(value, (str, float)): + if isinstance(value, str): + try: + value = float(value) + except ValueError: + raise error.PyAsn1Error( + 'Bad real value syntax: %s' % (value,) + ) + if self._inf and value in self._inf: + return value + else: + e = 0 + while int(value) != value: + value = value * 10 + e = e - 1 + return self.__normalizeBase10((int(value), 10, e)) + elif isinstance(value, Real): + return tuple(value) + raise error.PyAsn1Error( + 'Bad real value syntax: %s' % (value,) + ) + + def prettyOut(self, value): + if value in self._inf: + return '\'%s\'' % value + else: + return str(value) + + def prettyPrint(self, scope=0): + if self.isInfinity(): + return self.prettyOut(self._value) + else: + return str(float(self)) + + def isPlusInfinity(self): return self._value == self._plusInf + def isMinusInfinity(self): return self._value == self._minusInf + def isInfinity(self): return self._value in self._inf + + def __str__(self): return str(float(self)) + + def __add__(self, value): return self.clone(float(self) + value) + def __radd__(self, value): return self + value + def __mul__(self, value): return self.clone(float(self) * value) + def __rmul__(self, value): return self * value + def __sub__(self, value): return self.clone(float(self) - value) + def __rsub__(self, value): return self.clone(value - float(self)) + def __mod__(self, value): return self.clone(float(self) % value) + def __rmod__(self, value): return self.clone(value % float(self)) + def __pow__(self, value, modulo=None): return self.clone(pow(float(self), value, modulo)) + def __rpow__(self, value): return self.clone(pow(value, float(self))) + + if sys.version_info[0] <= 2: + def __div__(self, value): return self.clone(float(self) / value) + def __rdiv__(self, value): return self.clone(value / float(self)) + else: + def __truediv__(self, value): return self.clone(float(self) / value) + def __rtruediv__(self, value): return self.clone(value / float(self)) + def __divmod__(self, value): return self.clone(float(self) // value) + def __rdivmod__(self, value): return self.clone(value // float(self)) + + def __int__(self): return int(float(self)) + if sys.version_info[0] <= 2: + def __long__(self): return long(float(self)) + def __float__(self): + if self._value in self._inf: + return self._value + else: + return float( + self._value[0] * pow(self._value[1], self._value[2]) + ) + def __abs__(self): return self.clone(abs(float(self))) + def __pos__(self): return self.clone(+float(self)) + def __neg__(self): return self.clone(-float(self)) + def __round__(self, n=0): + r = round(float(self), n) + if n: + return self.clone(r) + else: + return r + def __floor__(self): return self.clone(math.floor(float(self))) + def __ceil__(self): return self.clone(math.ceil(float(self))) + if sys.version_info[0:2] > (2, 5): + def __trunc__(self): return self.clone(math.trunc(float(self))) + + def __lt__(self, value): return float(self) < value + def __le__(self, value): return float(self) <= value + def __eq__(self, value): return float(self) == value + def __ne__(self, value): return float(self) != value + def __gt__(self, value): return float(self) > value + def __ge__(self, value): return float(self) >= value + + if sys.version_info[0] <= 2: + def __nonzero__(self): return bool(float(self)) + else: + def __bool__(self): return bool(float(self)) + __hash__ = base.AbstractSimpleAsn1Item.__hash__ + + def __getitem__(self, idx): + if self._value in self._inf: + raise error.PyAsn1Error('Invalid infinite value operation') + else: + return self._value[idx] + +class Enumerated(Integer): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 0x0A) + ) + +# "Structured" ASN.1 types + +class SetOf(base.AbstractConstructedAsn1Item): + componentType = None + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ) + typeId = 1 + strictConstraints = False + + def _cloneComponentValues(self, myClone, cloneValueFlag): + idx = 0; l = len(self._componentValues) + while idx < l: + c = self._componentValues[idx] + if c is not None: + if isinstance(c, base.AbstractConstructedAsn1Item): + myClone.setComponentByPosition( + idx, c.clone(cloneValueFlag=cloneValueFlag) + ) + else: + myClone.setComponentByPosition(idx, c.clone()) + idx = idx + 1 + + def _verifyComponent(self, idx, value): + t = self._componentType + if t is None: + return + if not t.isSameTypeWith(value,matchConstraints=self.strictConstraints): + raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, t)) + if self.strictConstraints and \ + not t.isSuperTypeOf(value, matchTags=False): + raise error.PyAsn1Error('Component value is constraints-incompatible: %r vs %r' % (value, t)) + + def getComponentByPosition(self, idx): return self._componentValues[idx] + def setComponentByPosition(self, idx, value=None, verifyConstraints=True): + l = len(self._componentValues) + if idx >= l: + self._componentValues = self._componentValues + (idx-l+1)*[None] + if value is None: + if self._componentValues[idx] is None: + if self._componentType is None: + raise error.PyAsn1Error('Component type not defined') + self._componentValues[idx] = self._componentType.clone() + self._componentValuesSet = self._componentValuesSet + 1 + return self + elif not isinstance(value, base.Asn1Item): + if self._componentType is None: + raise error.PyAsn1Error('Component type not defined') + if isinstance(self._componentType, base.AbstractSimpleAsn1Item): + value = self._componentType.clone(value=value) + else: + raise error.PyAsn1Error('Instance value required') + if verifyConstraints: + if self._componentType is not None: + self._verifyComponent(idx, value) + self._verifySubtypeSpec(value, idx) + if self._componentValues[idx] is None: + self._componentValuesSet = self._componentValuesSet + 1 + self._componentValues[idx] = value + return self + + def getComponentTagMap(self): + if self._componentType is not None: + return self._componentType.getTagMap() + + def prettyPrint(self, scope=0): + scope = scope + 1 + r = self.__class__.__name__ + ':\n' + for idx in range(len(self._componentValues)): + r = r + ' '*scope + if self._componentValues[idx] is None: + r = r + '' + else: + r = r + self._componentValues[idx].prettyPrint(scope) + return r + + def prettyPrintType(self, scope=0): + scope = scope + 1 + r = '%s -> %s {\n' % (self.getTagSet(), self.__class__.__name__) + if self._componentType is not None: + r = r + ' '*scope + r = r + self._componentType.prettyPrintType(scope) + return r + '\n' + ' '*(scope-1) + '}' + +class SequenceOf(SetOf): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ) + typeId = 2 + +class SequenceAndSetBase(base.AbstractConstructedAsn1Item): + componentType = namedtype.NamedTypes() + strictConstraints = False + def __init__(self, componentType=None, tagSet=None, + subtypeSpec=None, sizeSpec=None): + if componentType is None: + componentType = self.componentType + base.AbstractConstructedAsn1Item.__init__( + self, componentType.clone(), tagSet, subtypeSpec, sizeSpec + ) + self._componentTypeLen = len(self._componentType) + + def __getitem__(self, idx): + if isinstance(idx, str): + return self.getComponentByName(idx) + else: + return base.AbstractConstructedAsn1Item.__getitem__(self, idx) + + def __setitem__(self, idx, value): + if isinstance(idx, str): + self.setComponentByName(idx, value) + else: + base.AbstractConstructedAsn1Item.__setitem__(self, idx, value) + + def _cloneComponentValues(self, myClone, cloneValueFlag): + idx = 0; l = len(self._componentValues) + while idx < l: + c = self._componentValues[idx] + if c is not None: + if isinstance(c, base.AbstractConstructedAsn1Item): + myClone.setComponentByPosition( + idx, c.clone(cloneValueFlag=cloneValueFlag) + ) + else: + myClone.setComponentByPosition(idx, c.clone()) + idx = idx + 1 + + def _verifyComponent(self, idx, value): + if idx >= self._componentTypeLen: + raise error.PyAsn1Error( + 'Component type error out of range' + ) + t = self._componentType[idx].getType() + if not t.isSameTypeWith(value,matchConstraints=self.strictConstraints): + raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, t)) + if self.strictConstraints and \ + not t.isSuperTypeOf(value, matchTags=False): + raise error.PyAsn1Error('Component value is constraints-incompatible: %r vs %r' % (value, t)) + + def getComponentByName(self, name): + return self.getComponentByPosition( + self._componentType.getPositionByName(name) + ) + def setComponentByName(self, name, value=None, verifyConstraints=True): + return self.setComponentByPosition( + self._componentType.getPositionByName(name),value,verifyConstraints + ) + + def getComponentByPosition(self, idx): + try: + return self._componentValues[idx] + except IndexError: + if idx < self._componentTypeLen: + return + raise + def setComponentByPosition(self, idx, value=None, + verifyConstraints=True, + exactTypes=False, + matchTags=True, + matchConstraints=True): + l = len(self._componentValues) + if idx >= l: + self._componentValues = self._componentValues + (idx-l+1)*[None] + if value is None: + if self._componentValues[idx] is None: + self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone() + self._componentValuesSet = self._componentValuesSet + 1 + return self + elif not isinstance(value, base.Asn1Item): + t = self._componentType.getTypeByPosition(idx) + if isinstance(t, base.AbstractSimpleAsn1Item): + value = t.clone(value=value) + else: + raise error.PyAsn1Error('Instance value required') + if verifyConstraints: + if self._componentTypeLen: + self._verifyComponent(idx, value) + self._verifySubtypeSpec(value, idx) + if self._componentValues[idx] is None: + self._componentValuesSet = self._componentValuesSet + 1 + self._componentValues[idx] = value + return self + + def getNameByPosition(self, idx): + if self._componentTypeLen: + return self._componentType.getNameByPosition(idx) + + def getDefaultComponentByPosition(self, idx): + if self._componentTypeLen and self._componentType[idx].isDefaulted: + return self._componentType[idx].getType() + + def getComponentType(self): + if self._componentTypeLen: + return self._componentType + + def setDefaultComponents(self): + if self._componentTypeLen == self._componentValuesSet: + return + idx = self._componentTypeLen + while idx: + idx = idx - 1 + if self._componentType[idx].isDefaulted: + if self.getComponentByPosition(idx) is None: + self.setComponentByPosition(idx) + elif not self._componentType[idx].isOptional: + if self.getComponentByPosition(idx) is None: + raise error.PyAsn1Error( + 'Uninitialized component #%s at %r' % (idx, self) + ) + + def prettyPrint(self, scope=0): + scope = scope + 1 + r = self.__class__.__name__ + ':\n' + for idx in range(len(self._componentValues)): + if self._componentValues[idx] is not None: + r = r + ' '*scope + componentType = self.getComponentType() + if componentType is None: + r = r + '' + else: + r = r + componentType.getNameByPosition(idx) + r = '%s=%s\n' % ( + r, self._componentValues[idx].prettyPrint(scope) + ) + return r + + def prettyPrintType(self, scope=0): + scope = scope + 1 + r = '%s -> %s {\n' % (self.getTagSet(), self.__class__.__name__) + for idx in range(len(self.componentType)): + r = r + ' '*scope + r = r + '"%s"' % self.componentType.getNameByPosition(idx) + r = '%s = %s\n' % ( + r, self._componentType.getTypeByPosition(idx).prettyPrintType(scope) + ) + return r + '\n' + ' '*(scope-1) + '}' + +class Sequence(SequenceAndSetBase): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x10) + ) + typeId = 3 + + def getComponentTagMapNearPosition(self, idx): + if self._componentType: + return self._componentType.getTagMapNearPosition(idx) + + def getComponentPositionNearType(self, tagSet, idx): + if self._componentType: + return self._componentType.getPositionNearType(tagSet, idx) + else: + return idx + +class Set(SequenceAndSetBase): + tagSet = baseTagSet = tag.initTagSet( + tag.Tag(tag.tagClassUniversal, tag.tagFormatConstructed, 0x11) + ) + typeId = 4 + + def getComponent(self, innerFlag=0): return self + + def getComponentByType(self, tagSet, innerFlag=0): + c = self.getComponentByPosition( + self._componentType.getPositionByType(tagSet) + ) + if innerFlag and isinstance(c, Set): + # get inner component by inner tagSet + return c.getComponent(1) + else: + # get outer component by inner tagSet + return c + + def setComponentByType(self, tagSet, value=None, innerFlag=0, + verifyConstraints=True): + idx = self._componentType.getPositionByType(tagSet) + t = self._componentType.getTypeByPosition(idx) + if innerFlag: # set inner component by inner tagSet + if t.getTagSet(): + return self.setComponentByPosition( + idx, value, verifyConstraints + ) + else: + t = self.setComponentByPosition(idx).getComponentByPosition(idx) + return t.setComponentByType( + tagSet, value, innerFlag, verifyConstraints + ) + else: # set outer component by inner tagSet + return self.setComponentByPosition( + idx, value, verifyConstraints + ) + + def getComponentTagMap(self): + if self._componentType: + return self._componentType.getTagMap(True) + + def getComponentPositionByType(self, tagSet): + if self._componentType: + return self._componentType.getPositionByType(tagSet) + +class Choice(Set): + tagSet = baseTagSet = tag.TagSet() # untagged + sizeSpec = constraint.ConstraintsIntersection( + constraint.ValueSizeConstraint(1, 1) + ) + typeId = 5 + _currentIdx = None + + def __eq__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] == other + return NotImplemented + def __ne__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] != other + return NotImplemented + def __lt__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] < other + return NotImplemented + def __le__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] <= other + return NotImplemented + def __gt__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] > other + return NotImplemented + def __ge__(self, other): + if self._componentValues: + return self._componentValues[self._currentIdx] >= other + return NotImplemented + if sys.version_info[0] <= 2: + def __nonzero__(self): return bool(self._componentValues) + else: + def __bool__(self): return bool(self._componentValues) + + def __len__(self): return self._currentIdx is not None and 1 or 0 + + def verifySizeSpec(self): + if self._currentIdx is None: + raise error.PyAsn1Error('Component not chosen') + else: + self._sizeSpec(' ') + + def _cloneComponentValues(self, myClone, cloneValueFlag): + try: + c = self.getComponent() + except error.PyAsn1Error: + pass + else: + if isinstance(c, Choice): + tagSet = c.getEffectiveTagSet() + else: + tagSet = c.getTagSet() + if isinstance(c, base.AbstractConstructedAsn1Item): + myClone.setComponentByType( + tagSet, c.clone(cloneValueFlag=cloneValueFlag) + ) + else: + myClone.setComponentByType(tagSet, c.clone()) + + def setComponentByPosition(self, idx, value=None, verifyConstraints=True): + l = len(self._componentValues) + if idx >= l: + self._componentValues = self._componentValues + (idx-l+1)*[None] + if self._currentIdx is not None: + self._componentValues[self._currentIdx] = None + if value is None: + if self._componentValues[idx] is None: + self._componentValues[idx] = self._componentType.getTypeByPosition(idx).clone() + self._componentValuesSet = 1 + self._currentIdx = idx + return self + elif not isinstance(value, base.Asn1Item): + value = self._componentType.getTypeByPosition(idx).clone( + value=value + ) + if verifyConstraints: + if self._componentTypeLen: + self._verifyComponent(idx, value) + self._verifySubtypeSpec(value, idx) + self._componentValues[idx] = value + self._currentIdx = idx + self._componentValuesSet = 1 + return self + + def getMinTagSet(self): + if self._tagSet: + return self._tagSet + else: + return self._componentType.genMinTagSet() + + def getEffectiveTagSet(self): + if self._tagSet: + return self._tagSet + else: + c = self.getComponent() + if isinstance(c, Choice): + return c.getEffectiveTagSet() + else: + return c.getTagSet() + + def getTagMap(self): + if self._tagSet: + return Set.getTagMap(self) + else: + return Set.getComponentTagMap(self) + + def getComponent(self, innerFlag=0): + if self._currentIdx is None: + raise error.PyAsn1Error('Component not chosen') + else: + c = self._componentValues[self._currentIdx] + if innerFlag and isinstance(c, Choice): + return c.getComponent(innerFlag) + else: + return c + + def getName(self, innerFlag=0): + if self._currentIdx is None: + raise error.PyAsn1Error('Component not chosen') + else: + if innerFlag: + c = self._componentValues[self._currentIdx] + if isinstance(c, Choice): + return c.getName(innerFlag) + return self._componentType.getNameByPosition(self._currentIdx) + + def setDefaultComponents(self): pass + +class Any(OctetString): + tagSet = baseTagSet = tag.TagSet() # untagged + typeId = 6 + + def getTagMap(self): + return tagmap.TagMap( + { self.getTagSet(): self }, + { eoo.endOfOctets.getTagSet(): eoo.endOfOctets }, + self + ) + +# XXX +# coercion rules? diff --git a/src/pyasn1/type/useful.py b/src/pyasn1/type/useful.py new file mode 100644 index 00000000..17665348 --- /dev/null +++ b/src/pyasn1/type/useful.py @@ -0,0 +1,17 @@ +# ASN.1 "useful" types +from pyasn1.type import char, tag + +class ObjectDescriptor(char.GraphicString): + tagSet = char.GraphicString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7) + ) + +class GeneralizedTime(char.VisibleString): + tagSet = char.VisibleString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24) + ) + +class UTCTime(char.VisibleString): + tagSet = char.VisibleString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23) + ) diff --git a/src/pyasn1_modules/__init__.py b/src/pyasn1_modules/__init__.py new file mode 100644 index 00000000..6598192c --- /dev/null +++ b/src/pyasn1_modules/__init__.py @@ -0,0 +1,2 @@ +# http://www.python.org/dev/peps/pep-0396/ +__version__ = '0.0.8' diff --git a/src/pyasn1_modules/pem.py b/src/pyasn1_modules/pem.py new file mode 100644 index 00000000..d8d81587 --- /dev/null +++ b/src/pyasn1_modules/pem.py @@ -0,0 +1,51 @@ +import base64, sys + +stSpam, stHam, stDump = 0, 1, 2 + +# The markers parameters is in form ('start1', 'stop1'), ('start2', 'stop2')... +# Return is (marker-index, substrate) +def readPemBlocksFromFile(fileObj, *markers): + startMarkers = dict(map(lambda x: (x[1],x[0]), + enumerate(map(lambda x: x[0], markers)))) + stopMarkers = dict(map(lambda x: (x[1],x[0]), + enumerate(map(lambda x: x[1], markers)))) + idx = -1; substrate = '' + state = stSpam + while 1: + certLine = fileObj.readline() + if not certLine: + break + certLine = certLine.strip() + if state == stSpam: + if certLine in startMarkers: + certLines = [] + idx = startMarkers[certLine] + state = stHam + continue + if state == stHam: + if certLine in stopMarkers and stopMarkers[certLine] == idx: + state = stDump + else: + certLines.append(certLine) + if state == stDump: + if sys.version_info[0] <= 2: + substrate = ''.join([ base64.b64decode(x) for x in certLines ]) + else: + substrate = ''.encode().join([ base64.b64decode(x.encode()) for x in certLines ]) + break + return idx, substrate + +# Backward compatibility routine +def readPemFromFile(fileObj, + startMarker='-----BEGIN CERTIFICATE-----', + endMarker='-----END CERTIFICATE-----'): + idx, substrate = readPemBlocksFromFile(fileObj, (startMarker, endMarker)) + return substrate + +def readBase64FromFile(fileObj): + if sys.version_info[0] <= 2: + return ''.join([ base64.b64decode(x) for x in fileObj.readlines() ]) + else: + return ''.encode().join( + [ base64.b64decode(x.encode()) for x in fileObj.readlines() ] + ) diff --git a/src/pyasn1_modules/rfc1155.py b/src/pyasn1_modules/rfc1155.py new file mode 100644 index 00000000..9e3c5cdb --- /dev/null +++ b/src/pyasn1_modules/rfc1155.py @@ -0,0 +1,73 @@ +# +# SNMPv1 message syntax +# +# ASN.1 source from: +# http://www.ietf.org/rfc/rfc1155.txt +# +# Sample captures from: +# http://wiki.wireshark.org/SampleCaptures/ +# +from pyasn1.type import univ, namedtype, namedval, tag, constraint + +class ObjectName(univ.ObjectIdentifier): pass + +class SimpleSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('number', univ.Integer()), + namedtype.NamedType('string', univ.OctetString()), + namedtype.NamedType('object', univ.ObjectIdentifier()), + namedtype.NamedType('empty', univ.Null()) + ) + +class IpAddress(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueSizeConstraint( + 4, 4 + ) +class NetworkAddress(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('internet', IpAddress()) + ) + +class Counter(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 1) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint( + 0, 4294967295 + ) +class Gauge(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint( + 0, 4294967295 + ) +class TimeTicks(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 3) + ) + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint( + 0, 4294967295 + ) +class Opaque(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 4) + ) + +class ApplicationSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('address', NetworkAddress()), + namedtype.NamedType('counter', Counter()), + namedtype.NamedType('gauge', Gauge()), + namedtype.NamedType('ticks', TimeTicks()), + namedtype.NamedType('arbitrary', Opaque()) + ) + +class ObjectSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('simple', SimpleSyntax()), + namedtype.NamedType('application-wide', ApplicationSyntax()) + ) diff --git a/src/pyasn1_modules/rfc1157.py b/src/pyasn1_modules/rfc1157.py new file mode 100644 index 00000000..6a36b06a --- /dev/null +++ b/src/pyasn1_modules/rfc1157.py @@ -0,0 +1,90 @@ +# +# SNMPv1 message syntax +# +# ASN.1 source from: +# http://www.ietf.org/rfc/rfc1157.txt +# +# Sample captures from: +# http://wiki.wireshark.org/SampleCaptures/ +# +from pyasn1.type import univ, namedtype, namedval, tag, constraint +from pyasn1_modules import rfc1155 + +class Version(univ.Integer): + namedValues = namedval.NamedValues( + ('version-1', 0) + ) + defaultValue = 0 + +class Community(univ.OctetString): pass + +class RequestID(univ.Integer): pass +class ErrorStatus(univ.Integer): + namedValues = namedval.NamedValues( + ('noError', 0), + ('tooBig', 1), + ('noSuchName', 2), + ('badValue', 3), + ('readOnly', 4), + ('genErr', 5) + ) +class ErrorIndex(univ.Integer): pass + +class VarBind(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', rfc1155.ObjectName()), + namedtype.NamedType('value', rfc1155.ObjectSyntax()) + ) +class VarBindList(univ.SequenceOf): + componentType = VarBind() + +class _RequestBase(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('request-id', RequestID()), + namedtype.NamedType('error-status', ErrorStatus()), + namedtype.NamedType('error-index', ErrorIndex()), + namedtype.NamedType('variable-bindings', VarBindList()) + ) + +class GetRequestPDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0) + ) +class GetNextRequestPDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) +class GetResponsePDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2) + ) +class SetRequestPDU(_RequestBase): + tagSet = _RequestBase.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3) + ) + +class TrapPDU(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('enterprise', univ.ObjectIdentifier()), + namedtype.NamedType('agent-addr', rfc1155.NetworkAddress()), + namedtype.NamedType('generic-trap', univ.Integer().clone(namedValues=namedval.NamedValues(('coldStart', 0), ('warmStart', 1), ('linkDown', 2), ('linkUp', 3), ('authenticationFailure', 4), ('egpNeighborLoss', 5), ('enterpriseSpecific', 6)))), + namedtype.NamedType('specific-trap', univ.Integer()), + namedtype.NamedType('time-stamp', rfc1155.TimeTicks()), + namedtype.NamedType('variable-bindings', VarBindList()) + ) + +class Pdus(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('get-request', GetRequestPDU()), + namedtype.NamedType('get-next-request', GetNextRequestPDU()), + namedtype.NamedType('get-response', GetResponsePDU()), + namedtype.NamedType('set-request', SetRequestPDU()), + namedtype.NamedType('trap', TrapPDU()) + ) + +class Message(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('community', Community()), + namedtype.NamedType('data', Pdus()) + ) diff --git a/src/pyasn1_modules/rfc1901.py b/src/pyasn1_modules/rfc1901.py new file mode 100644 index 00000000..8cd7e7d1 --- /dev/null +++ b/src/pyasn1_modules/rfc1901.py @@ -0,0 +1,15 @@ +# +# SNMPv2c message syntax +# +# ASN.1 source from: +# http://www.ietf.org/rfc/rfc1901.txt +# +from pyasn1.type import univ, namedtype, namedval + +class Message(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', univ.Integer(namedValues = namedval.NamedValues(('version-2c', 1)))), + namedtype.NamedType('community', univ.OctetString()), + namedtype.NamedType('data', univ.Any()) + ) + diff --git a/src/pyasn1_modules/rfc1902.py b/src/pyasn1_modules/rfc1902.py new file mode 100644 index 00000000..df0b0c3d --- /dev/null +++ b/src/pyasn1_modules/rfc1902.py @@ -0,0 +1,105 @@ +# +# SNMPv2c message syntax +# +# ASN.1 source from: +# http://www.ietf.org/rfc/rfc1902.txt +# +from pyasn1.type import univ, namedtype, namedval, tag, constraint + +class Integer(univ.Integer): + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueRangeConstraint( + -2147483648, 2147483647 + ) + +class Integer32(univ.Integer): + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueRangeConstraint( + -2147483648, 2147483647 + ) + +class OctetString(univ.OctetString): + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueSizeConstraint( + 0, 65535 + ) + +class IpAddress(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x00) + ) + subtypeSpec = univ.OctetString.subtypeSpec+constraint.ValueSizeConstraint( + 4, 4 + ) + +class Counter32(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x01) + ) + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueRangeConstraint( + 0, 4294967295 + ) + +class Gauge32(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x02) + ) + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueRangeConstraint( + 0, 4294967295 + ) + +class Unsigned32(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x02) + ) + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueRangeConstraint( + 0, 4294967295 + ) + +class TimeTicks(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x03) + ) + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueRangeConstraint( + 0, 4294967295 + ) + +class Opaque(univ.OctetString): + tagSet = univ.OctetString.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x04) + ) + +class Counter64(univ.Integer): + tagSet = univ.Integer.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 0x06) + ) + subtypeSpec = univ.Integer.subtypeSpec+constraint.ValueRangeConstraint( + 0, 18446744073709551615 + ) + +class Bits(univ.OctetString): pass + +class ObjectName(univ.ObjectIdentifier): pass + +class SimpleSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('integer-value', Integer()), + namedtype.NamedType('string-value', OctetString()), + namedtype.NamedType('objectID-value', univ.ObjectIdentifier()) + ) + +class ApplicationSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('ipAddress-value', IpAddress()), + namedtype.NamedType('counter-value', Counter32()), + namedtype.NamedType('timeticks-value', TimeTicks()), + namedtype.NamedType('arbitrary-value', Opaque()), + namedtype.NamedType('big-counter-value', Counter64()), +# This conflicts with Counter32 +# namedtype.NamedType('unsigned-integer-value', Unsigned32()), + namedtype.NamedType('gauge32-value', Gauge32()) + ) # BITS misplaced? + +class ObjectSyntax(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('simple', SimpleSyntax()), + namedtype.NamedType('application-wide', ApplicationSyntax()) + ) + diff --git a/src/pyasn1_modules/rfc1905.py b/src/pyasn1_modules/rfc1905.py new file mode 100644 index 00000000..bec60f8e --- /dev/null +++ b/src/pyasn1_modules/rfc1905.py @@ -0,0 +1,100 @@ +# +# SNMPv2c PDU syntax +# +# ASN.1 source from: +# http://www.ietf.org/rfc/rfc1905.txt +# +from pyasn1.type import univ, namedtype, namedval, tag, constraint +from pyasn1_modules import rfc1902 + +max_bindings = rfc1902.Integer(2147483647) + +class _BindValue(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('value', rfc1902.ObjectSyntax()), + namedtype.NamedType('unSpecified', univ.Null()), + namedtype.NamedType('noSuchObject', univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('noSuchInstance', univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('endOfMibView', univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + +class VarBind(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', rfc1902.ObjectName()), + namedtype.NamedType('', _BindValue()) + ) + +class VarBindList(univ.SequenceOf): + componentType = VarBind() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint( + 0, max_bindings + ) + +class PDU(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('request-id', rfc1902.Integer32()), + namedtype.NamedType('error-status', univ.Integer(namedValues=namedval.NamedValues(('noError', 0), ('tooBig', 1), ('noSuchName', 2), ('badValue', 3), ('readOnly', 4), ('genErr', 5), ('noAccess', 6), ('wrongType', 7), ('wrongLength', 8), ('wrongEncoding', 9), ('wrongValue', 10), ('noCreation', 11), ('inconsistentValue', 12), ('resourceUnavailable', 13), ('commitFailed', 14), ('undoFailed', 15), ('authorizationError', 16), ('notWritable', 17), ('inconsistentName', 18)))), + namedtype.NamedType('error-index', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, max_bindings))), + namedtype.NamedType('variable-bindings', VarBindList()) + ) + +class BulkPDU(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('request-id', rfc1902.Integer32()), + namedtype.NamedType('non-repeaters', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, max_bindings))), + namedtype.NamedType('max-repetitions', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, max_bindings))), + namedtype.NamedType('variable-bindings', VarBindList()) + ) + +class GetRequestPDU(PDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0) + ) + +class GetNextRequestPDU(PDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) + +class ResponsePDU(PDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2) + ) + +class SetRequestPDU(PDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3) + ) + +class GetBulkRequestPDU(BulkPDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 5) + ) + +class InformRequestPDU(PDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 6) + ) + +class SNMPv2TrapPDU(PDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 7) + ) + +class ReportPDU(PDU): + tagSet = PDU.tagSet.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 8) + ) + +class PDUs(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('get-request', GetRequestPDU()), + namedtype.NamedType('get-next-request', GetNextRequestPDU()), + namedtype.NamedType('get-bulk-request', GetBulkRequestPDU()), + namedtype.NamedType('response', ResponsePDU()), + namedtype.NamedType('set-request', SetRequestPDU()), + namedtype.NamedType('inform-request', InformRequestPDU()), + namedtype.NamedType('snmpV2-trap', SNMPv2TrapPDU()), + namedtype.NamedType('report', ReportPDU()) + ) + diff --git a/src/pyasn1_modules/rfc2251.py b/src/pyasn1_modules/rfc2251.py new file mode 100644 index 00000000..3074c67a --- /dev/null +++ b/src/pyasn1_modules/rfc2251.py @@ -0,0 +1,319 @@ +# +# LDAP message syntax +# +# ASN.1 source from: +# http://www.trl.ibm.com/projects/xml/xss4j/data/asn1/grammars/ldap.asn +# +# Sample captures from: +# http://wiki.wireshark.org/SampleCaptures/ +# +from pyasn1.type import tag, namedtype, namedval, univ, constraint,char,useful +from pyasn1.codec.der import decoder, encoder + +maxInt = univ.Integer(2147483647) + +class LDAPString(univ.OctetString): pass +class LDAPOID(univ.OctetString): pass + +class LDAPDN(LDAPString): pass +class RelativeLDAPDN(LDAPString): pass +class AttributeType(LDAPString): pass +class AttributeDescription(LDAPString): pass + +class AttributeDescriptionList(univ.SequenceOf): + componentType = AttributeDescription() + +class AttributeValue(univ.OctetString): pass + +class AssertionValue(univ.OctetString): pass + +class AttributeValueAssertion(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('attributeDesc', AttributeDescription()), + namedtype.NamedType('assertionValue', AssertionValue()) + ) + +class Attribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeDescription()), + namedtype.NamedType('vals', univ.SetOf(componentType=AttributeValue())) + ) + +class MatchingRuleId(LDAPString): pass + +class Control(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('controlType', LDAPOID()), + namedtype.DefaultedNamedType('criticality', univ.Boolean('False')), + namedtype.OptionalNamedType('controlValue', univ.OctetString()) + ) + +class Controls(univ.SequenceOf): + componentType = Control() + +class LDAPURL(LDAPString): pass + +class Referral(univ.SequenceOf): + componentType = LDAPURL() + +class SaslCredentials(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('mechanism', LDAPString()), + namedtype.OptionalNamedType('credentials', univ.OctetString()) + ) + +class AuthenticationChoice(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('simple', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('reserved-1', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('reserved-2', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.NamedType('sasl', SaslCredentials().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))) + ) + +class BindRequest(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 0) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, 127))), + namedtype.NamedType('name', LDAPDN()), + namedtype.NamedType('authentication', AuthenticationChoice()) + ) + +class PartialAttributeList(univ.SequenceOf): + componentType = univ.Sequence(componentType=namedtype.NamedTypes(namedtype.NamedType('type', AttributeDescription()), namedtype.NamedType('vals', univ.SetOf(componentType=AttributeValue())))) + +class SearchResultEntry(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 4) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('objectName', LDAPDN()), + namedtype.NamedType('attributes', PartialAttributeList()) + ) + +class MatchingRuleAssertion(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('matchingRule', MatchingRuleId().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('type', AttributeDescription().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.NamedType('matchValue', AssertionValue().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.DefaultedNamedType('dnAttributes', univ.Boolean('False').subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))) + ) + +class SubstringFilter(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeDescription()), + namedtype.NamedType('substrings', univ.SequenceOf(componentType=univ.Choice(componentType=namedtype.NamedTypes(namedtype.NamedType('initial', LDAPString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), namedtype.NamedType('any', LDAPString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), namedtype.NamedType('final', LDAPString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))))))) + ) + +# Ugly hack to handle recursive Filter reference (up to 3-levels deep). + +class Filter3(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('equalityMatch', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))), + namedtype.NamedType('substrings', SubstringFilter().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))), + namedtype.NamedType('greaterOrEqual', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 5))), + namedtype.NamedType('lessOrEqual', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 6))), + namedtype.NamedType('present', AttributeDescription().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))), + namedtype.NamedType('approxMatch', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 8))), + namedtype.NamedType('extensibleMatch', MatchingRuleAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 9))) + ) + +class Filter2(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('and', univ.SetOf(componentType=Filter3()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('or', univ.SetOf(componentType=Filter3()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.NamedType('not', Filter3().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))), + namedtype.NamedType('equalityMatch', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))), + namedtype.NamedType('substrings', SubstringFilter().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))), + namedtype.NamedType('greaterOrEqual', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 5))), + namedtype.NamedType('lessOrEqual', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 6))), + namedtype.NamedType('present', AttributeDescription().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))), + namedtype.NamedType('approxMatch', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 8))), + namedtype.NamedType('extensibleMatch', MatchingRuleAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 9))) + ) + +class Filter(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('and', univ.SetOf(componentType=Filter2()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('or', univ.SetOf(componentType=Filter2()).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.NamedType('not', Filter2().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))), + namedtype.NamedType('equalityMatch', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))), + namedtype.NamedType('substrings', SubstringFilter().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))), + namedtype.NamedType('greaterOrEqual', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 5))), + namedtype.NamedType('lessOrEqual', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 6))), + namedtype.NamedType('present', AttributeDescription().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))), + namedtype.NamedType('approxMatch', AttributeValueAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 8))), + namedtype.NamedType('extensibleMatch', MatchingRuleAssertion().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 9))) + ) + +# End of Filter hack + +class SearchRequest(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 3) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('baseObject', LDAPDN()), + namedtype.NamedType('scope', univ.Enumerated(namedValues=namedval.NamedValues(('baseObject', 0), ('singleLevel', 1), ('wholeSubtree', 2)))), + namedtype.NamedType('derefAliases', univ.Enumerated(namedValues=namedval.NamedValues(('neverDerefAliases', 0), ('derefInSearching', 1), ('derefFindingBaseObj', 2), ('derefAlways', 3)))), + namedtype.NamedType('sizeLimit', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, maxInt))), + namedtype.NamedType('timeLimit', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, maxInt))), + namedtype.NamedType('typesOnly', univ.Boolean()), + namedtype.NamedType('filter', Filter()), + namedtype.NamedType('attributes', AttributeDescriptionList()) + ) + +class UnbindRequest(univ.Null): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatSimple, 2) + ) + +class BindResponse(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 1) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('resultCode', univ.Enumerated(namedValues=namedval.NamedValues(('success', 0), ('operationsError', 1), ('protocolError', 2), ('timeLimitExceeded', 3), ('sizeLimitExceeded', 4), ('compareFalse', 5), ('compareTrue', 6), ('authMethodNotSupported', 7), ('strongAuthRequired', 8), ('reserved-9', 9), ('referral', 10), ('adminLimitExceeded', 11), ('unavailableCriticalExtension', 12), ('confidentialityRequired', 13), ('saslBindInProgress', 14), ('noSuchAttribute', 16), ('undefinedAttributeType', 17), ('inappropriateMatching', 18), ('constraintViolation', 19), ('attributeOrValueExists', 20), ('invalidAttributeSyntax', 21), ('noSuchObject', 32), ('aliasProblem', 33), ('invalidDNSyntax', 34), ('reserved-35', 35), ('aliasDereferencingProblem', 36), ('inappropriateAuthentication', 48), ('invalidCredentials', 49), ('insufficientAccessRights', 50), ('busy', 51), ('unavailable', 52), ('unwillingToPerform', 53), ('loopDetect', 54), ('namingViolation', 64), ('objectClassViolation', 65), ('notAllowedOnNonLeaf', 66), ('notAllowedOnRDN', 67), ('entryAlreadyExists', 68), ('objectClassModsProhibited', 69), ('reserved-70', 70), ('affectsMultipleDSAs', 71), ('other', 80), ('reserved-81', 81), ('reserved-82', 82), ('reserved-83', 83), ('reserved-84', 84), ('reserved-85', 85), ('reserved-86', 86), ('reserved-87', 87), ('reserved-88', 88), ('reserved-89', 89), ('reserved-90', 90)))), + namedtype.NamedType('matchedDN', LDAPDN()), + namedtype.NamedType('errorMessage', LDAPString()), + namedtype.OptionalNamedType('referral', Referral().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))), + namedtype.OptionalNamedType('serverSaslCreds', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 7))) + ) + +class LDAPResult(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('resultCode', univ.Enumerated(namedValues=namedval.NamedValues(('success', 0), ('operationsError', 1), ('protocolError', 2), ('timeLimitExceeded', 3), ('sizeLimitExceeded', 4), ('compareFalse', 5), ('compareTrue', 6), ('authMethodNotSupported', 7), ('strongAuthRequired', 8), ('reserved-9', 9), ('referral', 10), ('adminLimitExceeded', 11), ('unavailableCriticalExtension', 12), ('confidentialityRequired', 13), ('saslBindInProgress', 14), ('noSuchAttribute', 16), ('undefinedAttributeType', 17), ('inappropriateMatching', 18), ('constraintViolation', 19), ('attributeOrValueExists', 20), ('invalidAttributeSyntax', 21), ('noSuchObject', 32), ('aliasProblem', 33), ('invalidDNSyntax', 34), ('reserved-35', 35), ('aliasDereferencingProblem', 36), ('inappropriateAuthentication', 48), ('invalidCredentials', 49), ('insufficientAccessRights', 50), ('busy', 51), ('unavailable', 52), ('unwillingToPerform', 53), ('loopDetect', 54), ('namingViolation', 64), ('objectClassViolation', 65), ('notAllowedOnNonLeaf', 66), ('notAllowedOnRDN', 67), ('entryAlreadyExists', 68), ('objectClassModsProhibited', 69), ('reserved-70', 70), ('affectsMultipleDSAs', 71), ('other', 80), ('reserved-81', 81), ('reserved-82', 82), ('reserved-83', 83), ('reserved-84', 84), ('reserved-85', 85), ('reserved-86', 86), ('reserved-87', 87), ('reserved-88', 88), ('reserved-89', 89), ('reserved-90', 90)))), + namedtype.NamedType('matchedDN', LDAPDN()), + namedtype.NamedType('errorMessage', LDAPString()), + namedtype.OptionalNamedType('referral', Referral().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))) + ) + +class SearchResultReference(univ.SequenceOf): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 19) + ) + componentType = LDAPURL() + +class SearchResultDone(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 5) + ) + +class AttributeTypeAndValues(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeDescription()), + namedtype.NamedType('vals', univ.SetOf(componentType=AttributeValue())) + ) + +class ModifyRequest(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 6) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('object', LDAPDN()), + namedtype.NamedType('modification', univ.SequenceOf(componentType=univ.Sequence(componentType=namedtype.NamedTypes(namedtype.NamedType('operation', univ.Enumerated(namedValues=namedval.NamedValues(('add', 0), ('delete', 1), ('replace', 2)))), namedtype.NamedType('modification', AttributeTypeAndValues()))))) + ) + +class ModifyResponse(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 7) + ) + +class AttributeList(univ.SequenceOf): + componentType = univ.Sequence(componentType=namedtype.NamedTypes(namedtype.NamedType('type', AttributeDescription()), namedtype.NamedType('vals', univ.SetOf(componentType=AttributeValue())))) + +class AddRequest(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 8) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('entry', LDAPDN()), + namedtype.NamedType('attributes', AttributeList()) + ) + +class AddResponse(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 9) + ) + +class DelRequest(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 10) + ) + +class DelResponse(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 11) + ) + +class ModifyDNRequest(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 12) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('entry', LDAPDN()), + namedtype.NamedType('newrdn', RelativeLDAPDN()), + namedtype.NamedType('deleteoldrdn', univ.Boolean()), + namedtype.OptionalNamedType('newSuperior', LDAPDN().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + + ) + +class ModifyDNResponse(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 13) + ) + +class CompareRequest(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 14) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('entry', LDAPDN()), + namedtype.NamedType('ava', AttributeValueAssertion()) + ) + +class CompareResponse(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 15) + ) + +class AbandonRequest(LDAPResult): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 16) + ) + +class ExtendedRequest(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 23) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('requestName', LDAPOID().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('requestValue', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +class ExtendedResponse(univ.Sequence): + tagSet = univ.Sequence.tagSet.tagImplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 24) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('resultCode', univ.Enumerated(namedValues=namedval.NamedValues(('success', 0), ('operationsError', 1), ('protocolError', 2), ('timeLimitExceeded', 3), ('sizeLimitExceeded', 4), ('compareFalse', 5), ('compareTrue', 6), ('authMethodNotSupported', 7), ('strongAuthRequired', 8), ('reserved-9', 9), ('referral', 10), ('adminLimitExceeded', 11), ('unavailableCriticalExtension', 12), ('confidentialityRequired', 13), ('saslBindInProgress', 14), ('noSuchAttribute', 16), ('undefinedAttributeType', 17), ('inappropriateMatching', 18), ('constraintViolation', 19), ('attributeOrValueExists', 20), ('invalidAttributeSyntax', 21), ('noSuchObject', 32), ('aliasProblem', 33), ('invalidDNSyntax', 34), ('reserved-35', 35), ('aliasDereferencingProblem', 36), ('inappropriateAuthentication', 48), ('invalidCredentials', 49), ('insufficientAccessRights', 50), ('busy', 51), ('unavailable', 52), ('unwillingToPerform', 53), ('loopDetect', 54), ('namingViolation', 64), ('objectClassViolation', 65), ('notAllowedOnNonLeaf', 66), ('notAllowedOnRDN', 67), ('entryAlreadyExists', 68), ('objectClassModsProhibited', 69), ('reserved-70', 70), ('affectsMultipleDSAs', 71), ('other', 80), ('reserved-81', 81), ('reserved-82', 82), ('reserved-83', 83), ('reserved-84', 84), ('reserved-85', 85), ('reserved-86', 86), ('reserved-87', 87), ('reserved-88', 88), ('reserved-89', 89), ('reserved-90', 90)))), + namedtype.NamedType('matchedDN', LDAPDN()), + namedtype.NamedType('errorMessage', LDAPString()), + namedtype.OptionalNamedType('referral', Referral().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))), + + namedtype.OptionalNamedType('responseName', LDAPOID().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 10))), + namedtype.OptionalNamedType('response', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 11))) + ) + +class MessageID(univ.Integer): + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint( + 0, maxInt + ) + +class LDAPMessage(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('messageID', MessageID()), + namedtype.NamedType('protocolOp', univ.Choice(componentType=namedtype.NamedTypes(namedtype.NamedType('bindRequest', BindRequest()), namedtype.NamedType('bindResponse', BindResponse()), namedtype.NamedType('unbindRequest', UnbindRequest()), namedtype.NamedType('searchRequest', SearchRequest()), namedtype.NamedType('searchResEntry', SearchResultEntry()), namedtype.NamedType('searchResDone', SearchResultDone()), namedtype.NamedType('searchResRef', SearchResultReference()), namedtype.NamedType('modifyRequest', ModifyRequest()), namedtype.NamedType('modifyResponse', ModifyResponse()), namedtype.NamedType('addRequest', AddRequest()), namedtype.NamedType('addResponse', AddResponse()), namedtype.NamedType('delRequest', DelRequest()), namedtype.NamedType('delResponse', DelResponse()), namedtype.NamedType('modDNRequest', ModifyDNRequest()), namedtype.NamedType('modDNResponse', ModifyDNResponse()), namedtype.NamedType('compareRequest', CompareRequest()), namedtype.NamedType('compareResponse', CompareResponse()), namedtype.NamedType('abandonRequest', AbandonRequest()), namedtype.NamedType('extendedReq', ExtendedRequest()), namedtype.NamedType('extendedResp', ExtendedResponse())))), + namedtype.OptionalNamedType('controls', Controls().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) diff --git a/src/pyasn1_modules/rfc2314.py b/src/pyasn1_modules/rfc2314.py new file mode 100644 index 00000000..86b11fb3 --- /dev/null +++ b/src/pyasn1_modules/rfc2314.py @@ -0,0 +1,33 @@ +# +# PKCS#10 syntax +# +# ASN.1 source from: +# http://tools.ietf.org/html/rfc2314 +# +# Sample captures could be obtained with "openssl req" command +# +from pyasn1.type import tag, namedtype, namedval, univ, constraint +from pyasn1_modules.rfc2459 import * + +class Attributes(univ.SetOf): + componentType = Attribute() + +class Version(univ.Integer): pass + +class CertificationRequestInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('subject', Name()), + namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo()), + namedtype.NamedType('attributes', Attributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) + +class Signature(univ.BitString): pass +class SignatureAlgorithmIdentifier(AlgorithmIdentifier): pass + +class CertificationRequest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certificationRequestInfo', CertificationRequestInfo()), + namedtype.NamedType('signatureAlgorithm', SignatureAlgorithmIdentifier()), + namedtype.NamedType('signature', Signature()) + ) diff --git a/src/pyasn1_modules/rfc2315.py b/src/pyasn1_modules/rfc2315.py new file mode 100644 index 00000000..76bb9573 --- /dev/null +++ b/src/pyasn1_modules/rfc2315.py @@ -0,0 +1,205 @@ +# +# PKCS#7 message syntax +# +# ASN.1 source from: +# http://www.trl.ibm.com/projects/xml/xss4j/data/asn1/grammars/pkcs7.asn +# +# Sample captures from: +# openssl crl2pkcs7 -nocrl -certfile cert1.cer -out outfile.p7b +# +from pyasn1.type import tag,namedtype,namedval,univ,constraint,char,useful +from pyasn1_modules.rfc2459 import * + +class Attribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeType()), + namedtype.NamedType('values', univ.SetOf(componentType=AttributeValue())) + ) + +class AttributeValueAssertion(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('attributeType', AttributeType()), + namedtype.NamedType('attributeValue', AttributeValue()) + ) + +pkcs_7 = univ.ObjectIdentifier('1.2.840.113549.1.7') +data = univ.ObjectIdentifier('1.2.840.113549.1.7.1') +signedData = univ.ObjectIdentifier('1.2.840.113549.1.7.2') +envelopedData = univ.ObjectIdentifier('1.2.840.113549.1.7.3') +signedAndEnvelopedData = univ.ObjectIdentifier('1.2.840.113549.1.7.4') +digestedData = univ.ObjectIdentifier('1.2.840.113549.1.7.5') +encryptedData = univ.ObjectIdentifier('1.2.840.113549.1.7.6') + +class ContentType(univ.ObjectIdentifier): pass + +class ContentEncryptionAlgorithmIdentifier(AlgorithmIdentifier): pass + +class EncryptedContent(univ.OctetString): pass + +class EncryptedContentInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('contentType', ContentType()), + namedtype.NamedType('contentEncryptionAlgorithm', ContentEncryptionAlgorithmIdentifier()), + namedtype.OptionalNamedType('encryptedContent', EncryptedContent().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) + +class Version(univ.Integer): pass # overrides x509.Version + +class EncryptedData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('encryptedContentInfo', EncryptedContentInfo()) + ) + +class DigestAlgorithmIdentifier(AlgorithmIdentifier): pass + +class DigestAlgorithmIdentifiers(univ.SetOf): + componentType = DigestAlgorithmIdentifier() + +class Digest(univ.OctetString): pass + +class ContentInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('contentType', ContentType()), + namedtype.OptionalNamedType('content', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) + +class DigestedData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()), + namedtype.NamedType('contentInfo', ContentInfo()), + namedtype.NamedType('digest', Digest) + ) + +class IssuerAndSerialNumber(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('issuer', Name()), + namedtype.NamedType('serialNumber', CertificateSerialNumber()) + ) + +class KeyEncryptionAlgorithmIdentifier(AlgorithmIdentifier): pass + +class EncryptedKey(univ.OctetString): pass + +class RecipientInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()), + namedtype.NamedType('keyEncryptionAlgorithm', KeyEncryptionAlgorithmIdentifier()), + namedtype.NamedType('encryptedKey', EncryptedKey()) + ) + +class RecipientInfos(univ.SetOf): + componentType = RecipientInfo() + +class Attributes(univ.SetOf): + componentType = Attribute() + +class ExtendedCertificateInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('certificate', Certificate()), + namedtype.NamedType('attributes', Attributes()) + ) + +class SignatureAlgorithmIdentifier(AlgorithmIdentifier): pass + +class Signature(univ.BitString): pass + +class ExtendedCertificate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('extendedCertificateInfo', ExtendedCertificateInfo()), + namedtype.NamedType('signatureAlgorithm', SignatureAlgorithmIdentifier()), + namedtype.NamedType('signature', Signature()) + ) + +class ExtendedCertificateOrCertificate(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certificate', Certificate()), + namedtype.NamedType('extendedCertificate', ExtendedCertificate().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) + +class ExtendedCertificatesAndCertificates(univ.SetOf): + componentType = ExtendedCertificateOrCertificate() + +class SerialNumber(univ.Integer): pass + +class CRLEntry(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('userCertificate', SerialNumber()), + namedtype.NamedType('revocationDate', useful.UTCTime()) + ) + +class TBSCertificateRevocationList(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('signature', AlgorithmIdentifier()), + namedtype.NamedType('issuer', Name()), + namedtype.NamedType('lastUpdate', useful.UTCTime()), + namedtype.NamedType('nextUpdate', useful.UTCTime()), + namedtype.OptionalNamedType('revokedCertificates', univ.SequenceOf(componentType=CRLEntry())) + ) + +class CertificateRevocationList(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsCertificateRevocationList', TBSCertificateRevocationList()), + namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('signature', univ.BitString()) + ) + +class CertificateRevocationLists(univ.SetOf): + componentType = CertificateRevocationList() + +class DigestEncryptionAlgorithmIdentifier(AlgorithmIdentifier): pass + +class EncryptedDigest(univ.OctetString): pass + +class SignerInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('issuerAndSerialNumber', IssuerAndSerialNumber()), + namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()), + namedtype.OptionalNamedType('authenticatedAttributes', Attributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('digestEncryptionAlgorithm', DigestEncryptionAlgorithmIdentifier()), + namedtype.NamedType('encryptedDigest', EncryptedDigest()), + namedtype.OptionalNamedType('unauthenticatedAttributes', Attributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) + ) + +class SignerInfos(univ.SetOf): + componentType = SignerInfo() + +class SignedAndEnvelopedData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('recipientInfos', RecipientInfos()), + namedtype.NamedType('digestAlgorithms', DigestAlgorithmIdentifiers()), + namedtype.NamedType('encryptedContentInfo', EncryptedContentInfo()), + namedtype.OptionalNamedType('certificates', ExtendedCertificatesAndCertificates().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('crls', CertificateRevocationLists().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.NamedType('signerInfos', SignerInfos()) + ) + +class EnvelopedData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('recipientInfos', RecipientInfos()), + namedtype.NamedType('encryptedContentInfo', EncryptedContentInfo()) + ) + +class DigestInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('digestAlgorithm', DigestAlgorithmIdentifier()), + namedtype.NamedType('digest', Digest()) + ) + +class SignedData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('digestAlgorithms', DigestAlgorithmIdentifiers()), + namedtype.NamedType('contentInfo', ContentInfo()), + namedtype.OptionalNamedType('certificates', ExtendedCertificatesAndCertificates().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('crls', CertificateRevocationLists().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.NamedType('signerInfos', SignerInfos()) + ) + +class Data(univ.OctetString): pass diff --git a/src/pyasn1_modules/rfc2437.py b/src/pyasn1_modules/rfc2437.py new file mode 100644 index 00000000..3abf6dc3 --- /dev/null +++ b/src/pyasn1_modules/rfc2437.py @@ -0,0 +1,53 @@ +# +# PKCS#1 syntax +# +# ASN.1 source from: +# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2.asn +# +# Sample captures could be obtained with "openssl genrsa" command +# +from pyasn1.type import tag, namedtype, namedval, univ, constraint +from pyasn1_modules.rfc2459 import AlgorithmIdentifier + +pkcs_1 = univ.ObjectIdentifier('1.2.840.113549.1.1') +rsaEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.1') +md2WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.2') +md4WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.3') +md5WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.4') +sha1WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.5') +rsaOAEPEncryptionSET = univ.ObjectIdentifier('1.2.840.113549.1.1.6') +id_RSAES_OAEP = univ.ObjectIdentifier('1.2.840.113549.1.1.7') +id_mgf1 = univ.ObjectIdentifier('1.2.840.113549.1.1.8') +id_pSpecified = univ.ObjectIdentifier('1.2.840.113549.1.1.9') +id_sha1 = univ.ObjectIdentifier('1.3.14.3.2.26') + +MAX = 16 + +class Version(univ.Integer): pass + +class RSAPrivateKey(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('modulus', univ.Integer()), + namedtype.NamedType('publicExponent', univ.Integer()), + namedtype.NamedType('privateExponent', univ.Integer()), + namedtype.NamedType('prime1', univ.Integer()), + namedtype.NamedType('prime2', univ.Integer()), + namedtype.NamedType('exponent1', univ.Integer()), + namedtype.NamedType('exponent2', univ.Integer()), + namedtype.NamedType('coefficient', univ.Integer()) + ) + +class RSAPublicKey(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('modulus', univ.Integer()), + namedtype.NamedType('publicExponent', univ.Integer()) + ) + +# XXX defaults not set +class RSAES_OAEP_params(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('hashFunc', AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('maskGenFunc', AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.NamedType('pSourceFunc', AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))) + ) diff --git a/src/pyasn1_modules/rfc2459.py b/src/pyasn1_modules/rfc2459.py new file mode 100644 index 00000000..c52ab091 --- /dev/null +++ b/src/pyasn1_modules/rfc2459.py @@ -0,0 +1,904 @@ +# +# X.509 message syntax +# +# ASN.1 source from: +# http://www.trl.ibm.com/projects/xml/xss4j/data/asn1/grammars/x509.asn +# http://www.ietf.org/rfc/rfc2459.txt +# +# Sample captures from: +# http://wiki.wireshark.org/SampleCaptures/ +# +from pyasn1.type import tag,namedtype,namedval,univ,constraint,char,useful + +MAX = 64 # XXX ? + +# +# PKIX1Explicit88 +# + +# Upper Bounds +ub_name = univ.Integer(32768) +ub_common_name = univ.Integer(64) +ub_locality_name = univ.Integer(128) +ub_state_name = univ.Integer(128) +ub_organization_name = univ.Integer(64) +ub_organizational_unit_name = univ.Integer(64) +ub_title = univ.Integer(64) +ub_match = univ.Integer(128) +ub_emailaddress_length = univ.Integer(128) +ub_common_name_length = univ.Integer(64) +ub_country_name_alpha_length = univ.Integer(2) +ub_country_name_numeric_length = univ.Integer(3) +ub_domain_defined_attributes = univ.Integer(4) +ub_domain_defined_attribute_type_length = univ.Integer(8) +ub_domain_defined_attribute_value_length = univ.Integer(128) +ub_domain_name_length = univ.Integer(16) +ub_extension_attributes = univ.Integer(256) +ub_e163_4_number_length = univ.Integer(15) +ub_e163_4_sub_address_length = univ.Integer(40) +ub_generation_qualifier_length = univ.Integer(3) +ub_given_name_length = univ.Integer(16) +ub_initials_length = univ.Integer(5) +ub_integer_options = univ.Integer(256) +ub_numeric_user_id_length = univ.Integer(32) +ub_organization_name_length = univ.Integer(64) +ub_organizational_unit_name_length = univ.Integer(32) +ub_organizational_units = univ.Integer(4) +ub_pds_name_length = univ.Integer(16) +ub_pds_parameter_length = univ.Integer(30) +ub_pds_physical_address_lines = univ.Integer(6) +ub_postal_code_length = univ.Integer(16) +ub_surname_length = univ.Integer(40) +ub_terminal_id_length = univ.Integer(24) +ub_unformatted_address_length = univ.Integer(180) +ub_x121_address_length = univ.Integer(16) + +class UniversalString(char.UniversalString): pass +class BMPString(char.BMPString): pass +class UTF8String(char.UTF8String): pass + +id_pkix = univ.ObjectIdentifier('1.3.6.1.5.5.7') +id_pe = univ.ObjectIdentifier('1.3.6.1.5.5.7.1') +id_qt = univ.ObjectIdentifier('1.3.6.1.5.5.7.2') +id_kp = univ.ObjectIdentifier('1.3.6.1.5.5.7.3') +id_ad = univ.ObjectIdentifier('1.3.6.1.5.5.7.48') + +id_qt_cps = univ.ObjectIdentifier('1.3.6.1.5.5.7.2.1') +id_qt_unotice = univ.ObjectIdentifier('1.3.6.1.5.5.7.2.2') + +id_ad_ocsp = univ.ObjectIdentifier('1.3.6.1.5.5.7.48.1') +id_ad_caIssuers = univ.ObjectIdentifier('1.3.6.1.5.5.7.48.2') + +class AttributeValue(univ.Any): pass + +class AttributeType(univ.ObjectIdentifier): pass + +class AttributeTypeAndValue(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeType()), + namedtype.NamedType('value', AttributeValue()) + ) + +class Attribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', AttributeType()), + namedtype.NamedType('vals', univ.SetOf(componentType=AttributeValue())) + ) + +id_at = univ.ObjectIdentifier('2.5.4') +id_at_name = univ.ObjectIdentifier('2.5.4.41') +# preserve misspelled variable for compatibility +id_at_sutname = id_at_surname = univ.ObjectIdentifier('2.5.4.4') +id_at_givenName = univ.ObjectIdentifier('2.5.4.42') +id_at_initials = univ.ObjectIdentifier('2.5.4.43') +id_at_generationQualifier = univ.ObjectIdentifier('2.5.4.44') + +class X520name(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_name))) + ) + +id_at_commonName = univ.ObjectIdentifier('2.5.4.3') + +class X520CommonName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_common_name))) + ) + +id_at_localityName = univ.ObjectIdentifier('2.5.4.7') + +class X520LocalityName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_locality_name))) + ) + +id_at_stateOrProvinceName = univ.ObjectIdentifier('2.5.4.8') + +class X520StateOrProvinceName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_state_name))) + ) + +id_at_organizationName = univ.ObjectIdentifier('2.5.4.10') + +class X520OrganizationName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organization_name))) + ) + +id_at_organizationalUnitName = univ.ObjectIdentifier('2.5.4.11') + +class X520OrganizationalUnitName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_organizational_unit_name))) + ) + +id_at_title = univ.ObjectIdentifier('2.5.4.12') + +class X520Title(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_title))) + ) + +id_at_dnQualifier = univ.ObjectIdentifier('2.5.4.46') + +class X520dnQualifier(char.PrintableString): pass + +id_at_countryName = univ.ObjectIdentifier('2.5.4.6') + +class X520countryName(char.PrintableString): + subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(2, 2) + +pkcs_9 = univ.ObjectIdentifier('1.2.840.113549.1.9') + +emailAddress = univ.ObjectIdentifier('1.2.840.113549.1.9.1') + +class Pkcs9email(char.IA5String): + subtypeSpec = char.IA5String.subtypeSpec + constraint.ValueSizeConstraint(1, ub_emailaddress_length) + +# ---- + +class DSAPrivateKey(univ.Sequence): + """PKIX compliant DSA private key structure""" + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', univ.Integer(namedValues=namedval.NamedValues(('v1', 0)))), + namedtype.NamedType('p', univ.Integer()), + namedtype.NamedType('q', univ.Integer()), + namedtype.NamedType('g', univ.Integer()), + namedtype.NamedType('public', univ.Integer()), + namedtype.NamedType('private', univ.Integer()) + ) + +# ---- + +class RelativeDistinguishedName(univ.SetOf): + componentType = AttributeTypeAndValue() + +class RDNSequence(univ.SequenceOf): + componentType = RelativeDistinguishedName() + +class Name(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('', RDNSequence()) + ) + +class DirectoryString(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('teletexString', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('printableString', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('universalString', char.UniversalString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + namedtype.NamedType('ia5String', char.IA5String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) # hm, this should not be here!? XXX + ) + +# certificate and CRL specific structures begin here + +class AlgorithmIdentifier(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('algorithm', univ.ObjectIdentifier()), + namedtype.OptionalNamedType('parameters', univ.Any()) + ) + +class Extension(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('extnID', univ.ObjectIdentifier()), + namedtype.DefaultedNamedType('critical', univ.Boolean('False')), + namedtype.NamedType('extnValue', univ.Any()) + ) + +class Extensions(univ.SequenceOf): + componentType = Extension() + sizeSpec = univ.SequenceOf.sizeSpec + constraint.ValueSizeConstraint(1, MAX) + +class SubjectPublicKeyInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('algorithm', AlgorithmIdentifier()), + namedtype.NamedType('subjectPublicKey', univ.BitString()) + ) + +class UniqueIdentifier(univ.BitString): pass + +class Time(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('utcTime', useful.UTCTime()), + namedtype.NamedType('generalTime', useful.GeneralizedTime()) + ) + +class Validity(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('notBefore', Time()), + namedtype.NamedType('notAfter', Time()) + ) + +class CertificateSerialNumber(univ.Integer): pass + +class Version(univ.Integer): + namedValues = namedval.NamedValues( + ('v1', 0), ('v2', 1), ('v3', 2) + ) + +class TBSCertificate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', Version('v1').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('serialNumber', CertificateSerialNumber()), + namedtype.NamedType('signature', AlgorithmIdentifier()), + namedtype.NamedType('issuer', Name()), + namedtype.NamedType('validity', Validity()), + namedtype.NamedType('subject', Name()), + namedtype.NamedType('subjectPublicKeyInfo', SubjectPublicKeyInfo()), + namedtype.OptionalNamedType('issuerUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('subjectUniqueID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('extensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))) + ) + +class Certificate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsCertificate', TBSCertificate()), + namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('signatureValue', univ.BitString()) + ) + +# CRL structures + +class RevokedCertificate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('userCertificate', CertificateSerialNumber()), + namedtype.NamedType('revocationDate', Time()), + namedtype.OptionalNamedType('crlEntryExtensions', Extensions()) + ) + +class TBSCertList(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('version', Version()), + namedtype.NamedType('signature', AlgorithmIdentifier()), + namedtype.NamedType('issuer', Name()), + namedtype.NamedType('thisUpdate', Time()), + namedtype.OptionalNamedType('nextUpdate', Time()), + namedtype.OptionalNamedType('revokedCertificates', univ.SequenceOf(componentType=RevokedCertificate())), + namedtype.OptionalNamedType('crlExtensions', Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) + +class CertificateList(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsCertList', TBSCertList()), + namedtype.NamedType('signatureAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('signature', univ.BitString()) + ) + +# Algorithm OIDs and parameter structures + +pkcs_1 = univ.ObjectIdentifier('1.2.840.113549.1.1') +rsaEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.1') +md2WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.2') +md5WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.4') +sha1WithRSAEncryption = univ.ObjectIdentifier('1.2.840.113549.1.1.5') +id_dsa_with_sha1 = univ.ObjectIdentifier('1.2.840.10040.4.3') + +class Dss_Sig_Value(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('r', univ.Integer()), + namedtype.NamedType('s', univ.Integer()) + ) + +dhpublicnumber = univ.ObjectIdentifier('1.2.840.10046.2.1') + +class ValidationParms(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('seed', univ.BitString()), + namedtype.NamedType('pgenCounter', univ.Integer()) + ) + +class DomainParameters(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('p', univ.Integer()), + namedtype.NamedType('g', univ.Integer()), + namedtype.NamedType('q', univ.Integer()), + namedtype.NamedType('j', univ.Integer()), + namedtype.OptionalNamedType('validationParms', ValidationParms()) + ) + +id_dsa = univ.ObjectIdentifier('1.2.840.10040.4.1') + +class Dss_Parms(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('p', univ.Integer()), + namedtype.NamedType('q', univ.Integer()), + namedtype.NamedType('g', univ.Integer()) + ) + +# x400 address syntax starts here + +teletex_domain_defined_attributes = univ.Integer(6) + +class TeletexDomainDefinedAttribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_defined_attribute_type_length))), + namedtype.NamedType('value', char.TeletexString()) + ) + +class TeletexDomainDefinedAttributes(univ.SequenceOf): + componentType = TeletexDomainDefinedAttribute() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_domain_defined_attributes) + +terminal_type = univ.Integer(23) + +class TerminalType(univ.Integer): + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueSizeConstraint(0, ub_integer_options) + namedValues = namedval.NamedValues( + ('telex', 3), + ('teletelex', 4), + ('g3-facsimile', 5), + ('g4-facsimile', 6), + ('ia5-terminal', 7), + ('videotex', 8) + ) + +class PresentationAddress(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('pSelector', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('sSelector', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('tSelector', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('nAddresses', univ.SetOf(componentType=univ.OctetString()).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3), subtypeSpec=constraint.ValueSizeConstraint(1, MAX))), + ) + +extended_network_address = univ.Integer(22) + +class E163_4_address(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('number', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_e163_4_number_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('sub-address', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_e163_4_sub_address_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +class ExtendedNetworkAddress(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('e163-4-address', E163_4_address()), + namedtype.NamedType('psap-address', PresentationAddress().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + +class PDSParameter(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('printable-string', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_parameter_length))), + namedtype.OptionalNamedType('teletex-string', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_parameter_length))) + ) + +local_postal_attributes = univ.Integer(21) + +class LocalPostalAttributes(PDSParameter): pass + +class UniquePostalName(PDSParameter): pass + +unique_postal_name = univ.Integer(20) + +poste_restante_address = univ.Integer(19) + +class PosteRestanteAddress(PDSParameter): pass + +post_office_box_address = univ.Integer(18) + +class PostOfficeBoxAddress(PDSParameter): pass + +street_address = univ.Integer(17) + +class StreetAddress(PDSParameter): pass + +class UnformattedPostalAddress(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('printable-address', univ.SequenceOf(componentType=char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_parameter_length)).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_pds_physical_address_lines)))), + namedtype.OptionalNamedType('teletex-string', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_unformatted_address_length))) + ) + +physical_delivery_office_name = univ.Integer(10) + +class PhysicalDeliveryOfficeName(PDSParameter): pass + +physical_delivery_office_number = univ.Integer(11) + +class PhysicalDeliveryOfficeNumber(PDSParameter): pass + +extension_OR_address_components = univ.Integer(12) + +class ExtensionORAddressComponents(PDSParameter): pass + +physical_delivery_personal_name = univ.Integer(13) + +class PhysicalDeliveryPersonalName(PDSParameter): pass + +physical_delivery_organization_name = univ.Integer(14) + +class PhysicalDeliveryOrganizationName(PDSParameter): pass + +extension_physical_delivery_address_components = univ.Integer(15) + +class ExtensionPhysicalDeliveryAddressComponents(PDSParameter): pass + +unformatted_postal_address = univ.Integer(16) + +postal_code = univ.Integer(9) + +class PostalCode(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('numeric-code', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_postal_code_length))), + namedtype.NamedType('printable-code', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_postal_code_length))) + ) + +class PhysicalDeliveryCountryName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('x121-dcc-code', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_numeric_length, ub_country_name_numeric_length))), + namedtype.NamedType('iso-3166-alpha2-code', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_alpha_length, ub_country_name_alpha_length))) + ) + +class PDSName(char.PrintableString): + subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_pds_name_length) + +physical_delivery_country_name = univ.Integer(8) + +class TeletexOrganizationalUnitName(char.TeletexString): + subtypeSpec = char.TeletexString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_unit_name_length) + +pds_name = univ.Integer(7) + +teletex_organizational_unit_names = univ.Integer(5) + +class TeletexOrganizationalUnitNames(univ.SequenceOf): + componentType = TeletexOrganizationalUnitName() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_units) + +teletex_personal_name = univ.Integer(4) + +class TeletexPersonalName(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.NamedType('surname', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_surname_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('given-name', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_given_name_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('initials', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_initials_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('generation-qualifier', char.TeletexString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_generation_qualifier_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))) + ) + +teletex_organization_name = univ.Integer(3) + +class TeletexOrganizationName(char.TeletexString): + subtypeSpec = char.TeletexString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organization_name_length) + +teletex_common_name = univ.Integer(2) + +class TeletexCommonName(char.TeletexString): + subtypeSpec = char.TeletexString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_common_name_length) + +class CommonName(char.PrintableString): + subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_common_name_length) + +common_name = univ.Integer(1) + +class ExtensionAttribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('extension-attribute-type', univ.Integer().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, ub_extension_attributes), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('extension-attribute-value', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +class ExtensionAttributes(univ.SetOf): + componentType = ExtensionAttribute() + subtypeSpec = univ.SetOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_extension_attributes) + +class BuiltInDomainDefinedAttribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_defined_attribute_type_length))), + namedtype.NamedType('value', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_defined_attribute_value_length))) + ) + +class BuiltInDomainDefinedAttributes(univ.SequenceOf): + componentType = BuiltInDomainDefinedAttribute() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_domain_defined_attributes) + +class OrganizationalUnitName(char.PrintableString): + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_unit_name_length) + +class OrganizationalUnitNames(univ.SequenceOf): + componentType = OrganizationalUnitName() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organizational_units) + +class PersonalName(univ.Set): + componentType = namedtype.NamedTypes( + namedtype.NamedType('surname', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_surname_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('given-name', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_given_name_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('initials', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_initials_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('generation-qualifier', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_generation_qualifier_length), explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))) + ) + +class NumericUserIdentifier(char.NumericString): + subtypeSpec = char.NumericString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_numeric_user_id_length) + +class OrganizationName(char.PrintableString): + subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_organization_name_length) + +class PrivateDomainName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('numeric', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_name_length))), + namedtype.NamedType('printable', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, ub_domain_name_length))) + ) + +class TerminalIdentifier(char.PrintableString): + subtypeSpec = char.PrintableString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_terminal_id_length) + +class X121Address(char.NumericString): + subtypeSpec = char.NumericString.subtypeSpec + constraint.ValueSizeConstraint(1, ub_x121_address_length) + +class NetworkAddress(X121Address): pass + +class AdministrationDomainName(univ.Choice): + tagSet = univ.Choice.tagSet.tagExplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 2) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('numeric', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, ub_domain_name_length))), + namedtype.NamedType('printable', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, ub_domain_name_length))) + ) + +class CountryName(univ.Choice): + tagSet = univ.Choice.tagSet.tagExplicitly( + tag.Tag(tag.tagClassApplication, tag.tagFormatConstructed, 1) + ) + componentType = namedtype.NamedTypes( + namedtype.NamedType('x121-dcc-code', char.NumericString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_numeric_length, ub_country_name_numeric_length))), + namedtype.NamedType('iso-3166-alpha2-code', char.PrintableString().subtype(subtypeSpec=constraint.ValueSizeConstraint(ub_country_name_alpha_length, ub_country_name_alpha_length))) + ) + +class BuiltInStandardAttributes(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('country-name', CountryName()), + namedtype.OptionalNamedType('administration-domain-name', AdministrationDomainName()), + namedtype.OptionalNamedType('network-address', NetworkAddress().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('terminal-identifier', TerminalIdentifier().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('private-domain-name', PrivateDomainName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('organization-name', OrganizationName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.OptionalNamedType('numeric-user-identifier', NumericUserIdentifier().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))), + namedtype.OptionalNamedType('personal-name', PersonalName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))), + namedtype.OptionalNamedType('organizational-unit-names', OrganizationalUnitNames().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6))) + ) + +class ORAddress(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('built-in-standard-attributes', BuiltInStandardAttributes()), + namedtype.OptionalNamedType('built-in-domain-defined-attributes', BuiltInDomainDefinedAttributes()), + namedtype.OptionalNamedType('extension-attributes', ExtensionAttributes()) + ) + +# +# PKIX1Implicit88 +# + +id_ce_invalidityDate = univ.ObjectIdentifier('2.5.29.24') + +class InvalidityDate(useful.GeneralizedTime): pass + +id_holdinstruction_none = univ.ObjectIdentifier('2.2.840.10040.2.1') +id_holdinstruction_callissuer = univ.ObjectIdentifier('2.2.840.10040.2.2') +id_holdinstruction_reject = univ.ObjectIdentifier('2.2.840.10040.2.3') + +holdInstruction = univ.ObjectIdentifier('2.2.840.10040.2') + +id_ce_holdInstructionCode = univ.ObjectIdentifier('2.5.29.23') + +class HoldInstructionCode(univ.ObjectIdentifier): pass + +id_ce_cRLReasons = univ.ObjectIdentifier('2.5.29.21') + +class CRLReason(univ.Enumerated): + namedValues = namedval.NamedValues( + ('unspecified', 0), + ('keyCompromise', 1), + ('cACompromise', 2), + ('affiliationChanged', 3), + ('superseded', 4), + ('cessationOfOperation', 5), + ('certificateHold', 6), + ('removeFromCRL', 8) + ) + +id_ce_cRLNumber = univ.ObjectIdentifier('2.5.29.20') + +class CRLNumber(univ.Integer): + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(0, MAX) + +class BaseCRLNumber(CRLNumber): pass + +id_kp_serverAuth = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.1') +id_kp_clientAuth = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.2') +id_kp_codeSigning = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.3') +id_kp_emailProtection = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.4') +id_kp_ipsecEndSystem = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.5') +id_kp_ipsecTunnel = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.6') +id_kp_ipsecUser = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.7') +id_kp_timeStamping = univ.ObjectIdentifier('1.3.6.1.5.5.7.3.8') +id_pe_authorityInfoAccess = univ.ObjectIdentifier('1.3.6.1.5.5.7.1.1') +id_ce_extKeyUsage = univ.ObjectIdentifier('2.5.29.37') + +class KeyPurposeId(univ.ObjectIdentifier): pass + +class ExtKeyUsageSyntax(univ.SequenceOf): + componentType = KeyPurposeId() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +class ReasonFlags(univ.BitString): + namedValues = namedval.NamedValues( + ('unused', 0), + ('keyCompromise', 1), + ('cACompromise', 2), + ('affiliationChanged', 3), + ('superseded', 4), + ('cessationOfOperation', 5), + ('certificateHold', 6) + ) + + +class SkipCerts(univ.Integer): + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueSizeConstraint(0, MAX) + +id_ce_policyConstraints = univ.ObjectIdentifier('2.5.29.36') + +class PolicyConstraints(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('requireExplicitPolicy', SkipCerts().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('inhibitPolicyMapping', SkipCerts().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) + ) + +id_ce_basicConstraints = univ.ObjectIdentifier('2.5.29.19') + +class BasicConstraints(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('cA', univ.Boolean(False)), + namedtype.OptionalNamedType('pathLenConstraint', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, MAX))) + ) + +id_ce_subjectDirectoryAttributes = univ.ObjectIdentifier('2.5.29.9') + +class SubjectDirectoryAttributes(univ.SequenceOf): + componentType = Attribute() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +class EDIPartyName(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('nameAssigner', DirectoryString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('partyName', DirectoryString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +class AnotherName(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('type-id', univ.ObjectIdentifier()), + namedtype.NamedType('value', univ.Any().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + +class GeneralName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('otherName', AnotherName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('rfc822Name', char.IA5String().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('dNSName', char.IA5String().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.NamedType('x400Address', ORAddress().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.NamedType('directoryName', Name().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))), + namedtype.NamedType('ediPartyName', EDIPartyName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))), + namedtype.NamedType('uniformResourceIdentifier', char.IA5String().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6))), + namedtype.NamedType('iPAddress', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))), + namedtype.NamedType('registeredID', univ.ObjectIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8))) + ) + +class GeneralNames(univ.SequenceOf): + componentType = GeneralName() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +class AccessDescription(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('accessMethod', univ.ObjectIdentifier()), + namedtype.NamedType('accessLocation', GeneralName()) + ) + +class AuthorityInfoAccessSyntax(univ.SequenceOf): + componentType = AccessDescription() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +id_ce_deltaCRLIndicator = univ.ObjectIdentifier('2.5.29.27') + +class DistributionPointName(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('fullName', GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('nameRelativeToCRLIssuer', RelativeDistinguishedName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) + ) + +class DistributionPoint(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('distributionPoint', DistributionPointName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('reasons', ReasonFlags().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('cRLIssuer', GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))) + ) +class BaseDistance(univ.Integer): + subtypeSpec = univ.Integer.subtypeSpec + constraint.ValueRangeConstraint(0, MAX) + +id_ce_cRLDistributionPoints = univ.ObjectIdentifier('2.5.29.31') + +class CRLDistPointsSyntax(univ.SequenceOf): + componentType = DistributionPoint() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) +id_ce_issuingDistributionPoint = univ.ObjectIdentifier('2.5.29.28') + +class IssuingDistributionPoint(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('distributionPoint', DistributionPointName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('onlyContainsUserCerts', univ.Boolean(False).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('onlyContainsCACerts', univ.Boolean(False).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('onlySomeReasons', ReasonFlags().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.NamedType('indirectCRL', univ.Boolean(False).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))) + ) + +class GeneralSubtree(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('base', GeneralName()), + namedtype.DefaultedNamedType('minimum', BaseDistance(0).subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('maximum', BaseDistance().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) + ) + +class GeneralSubtrees(univ.SequenceOf): + componentType = GeneralSubtree() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +id_ce_nameConstraints = univ.ObjectIdentifier('2.5.29.30') + +class NameConstraints(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('permittedSubtrees', GeneralSubtrees().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('excludedSubtrees', GeneralSubtrees().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))) + ) + + +class DisplayText(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('visibleString', char.VisibleString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 200))), + namedtype.NamedType('bmpString', char.BMPString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 200))), + namedtype.NamedType('utf8String', char.UTF8String().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 200))) + ) + +class NoticeReference(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('organization', DisplayText()), + namedtype.NamedType('noticeNumbers', univ.SequenceOf(componentType=univ.Integer())) + ) + +class UserNotice(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('noticeRef', NoticeReference()), + namedtype.OptionalNamedType('explicitText', DisplayText()) + ) + +class CPSuri(char.IA5String): pass + +class PolicyQualifierId(univ.ObjectIdentifier): + subtypeSpec = univ.ObjectIdentifier.subtypeSpec + constraint.SingleValueConstraint(id_qt_cps, id_qt_unotice) + +class CertPolicyId(univ.ObjectIdentifier): pass + +class PolicyQualifierInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('policyQualifierId', PolicyQualifierId()), + namedtype.NamedType('qualifier', univ.Any()) + ) + +id_ce_certificatePolicies = univ.ObjectIdentifier('2.5.29.32') + +class PolicyInformation(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('policyIdentifier', CertPolicyId()), + namedtype.OptionalNamedType('policyQualifiers', univ.SequenceOf(componentType=PolicyQualifierInfo()).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) + ) + +class CertificatePolicies(univ.SequenceOf): + componentType = PolicyInformation() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +id_ce_policyMappings = univ.ObjectIdentifier('2.5.29.33') + +class PolicyMapping(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('issuerDomainPolicy', CertPolicyId()), + namedtype.NamedType('subjectDomainPolicy', CertPolicyId()) + ) + +class PolicyMappings(univ.SequenceOf): + componentType = PolicyMapping() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +id_ce_privateKeyUsagePeriod = univ.ObjectIdentifier('2.5.29.16') + +class PrivateKeyUsagePeriod(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('notBefore', useful.GeneralizedTime().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('notAfter', useful.GeneralizedTime().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +id_ce_keyUsage = univ.ObjectIdentifier('2.5.29.15') + +class KeyUsage(univ.BitString): + namedValues = namedval.NamedValues( + ('digitalSignature', 0), + ('nonRepudiation', 1), + ('keyEncipherment', 2), + ('dataEncipherment', 3), + ('keyAgreement', 4), + ('keyCertSign', 5), + ('cRLSign', 6), + ('encipherOnly', 7), + ('decipherOnly', 8) + ) + +id_ce = univ.ObjectIdentifier('2.5.29') + +id_ce_authorityKeyIdentifier = univ.ObjectIdentifier('2.5.29.35') + +class KeyIdentifier(univ.OctetString): pass + +id_ce_subjectKeyIdentifier = univ.ObjectIdentifier('2.5.29.14') + +class SubjectKeyIdentifier(KeyIdentifier): pass + +class AuthorityKeyIdentifier(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('keyIdentifier', KeyIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('authorityCertIssuer', GeneralNames().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('authorityCertSerialNumber', CertificateSerialNumber().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + +id_ce_certificateIssuer = univ.ObjectIdentifier('2.5.29.29') + +class CertificateIssuer(GeneralNames): pass + +id_ce_subjectAltName = univ.ObjectIdentifier('2.5.29.17') + +class SubjectAltName(GeneralNames): pass + +id_ce_issuerAltName = univ.ObjectIdentifier('2.5.29.18') + +class IssuerAltName(GeneralNames): pass diff --git a/src/pyasn1_modules/rfc2511.py b/src/pyasn1_modules/rfc2511.py new file mode 100644 index 00000000..2fc592ce --- /dev/null +++ b/src/pyasn1_modules/rfc2511.py @@ -0,0 +1,176 @@ +# +# X.509 certificate Request Message Format (CRMF) syntax +# +# ASN.1 source from: +# http://tools.ietf.org/html/rfc2511 +# +# Sample captures could be obtained with OpenSSL +# +from pyasn1.type import tag, namedtype, namedval, univ, constraint, char,useful +from pyasn1_modules.rfc2459 import * +from pyasn1_modules import rfc2315 + +MAX=16 + +id_pkix = univ.ObjectIdentifier('1.3.6.1.5.5.7') +id_pkip = univ.ObjectIdentifier('1.3.6.1.5.5.7.5') +id_regCtrl = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.1') +id_regCtrl_regToken = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.1.1') +id_regCtrl_authenticator = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.1.2') +id_regCtrl_pkiPublicationInfo = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.1.3') +id_regCtrl_pkiArchiveOptions = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.1.4') +id_regCtrl_oldCertID = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.1.5') +id_regCtrl_protocolEncrKey = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.1.6') +id_regInfo = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.2') +id_regInfo_utf8Pairs = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.2.1') +id_regInfo_certReq = univ.ObjectIdentifier('1.3.6.1.5.5.7.5.2.2') + +# This should be in PKIX Certificate Extensions module + +class GeneralName(univ.OctetString): pass + +# end of PKIX Certificate Extensions module + +class UTF8Pairs(char.UTF8String): pass + +class ProtocolEncrKey(SubjectPublicKeyInfo): pass + +class CertId(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('issuer', GeneralName()), + namedtype.NamedType('serialNumber', univ.Integer()) + ) + +class OldCertId(CertId): pass + +class KeyGenParameters(univ.OctetString): pass + +class EncryptedValue(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('intendedAlg', AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType('symmAlg', AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.OptionalNamedType('encSymmKey', univ.BitString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))), + namedtype.OptionalNamedType('keyAlg', AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))), + namedtype.OptionalNamedType('valueHint', univ.OctetString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))), + namedtype.NamedType('encValue', univ.BitString()) + ) + +class EncryptedKey(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('encryptedValue', EncryptedValue()), + namedtype.NamedType('envelopedData', rfc2315.EnvelopedData().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) + +class PKIArchiveOptions(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('encryptedPrivKey', EncryptedKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('keyGenParameters', KeyGenParameters().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('archiveRemGenPrivKey', univ.Boolean().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + +class SinglePubInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('pubMethod', univ.Integer(namedValues=namedval.NamedValues(('dontCare', 0), ('x500', 1), ('web', 2), ('ldap', 3)))), + namedtype.OptionalNamedType('pubLocation', GeneralName()) + ) + +class PKIPublicationInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('action', univ.Integer(namedValues=namedval.NamedValues(('dontPublish', 0), ('pleasePublish', 1)))), + namedtype.OptionalNamedType('pubInfos', univ.SequenceOf(componentType=SinglePubInfo()).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) + ) + +class Authenticator(char.UTF8String): pass +class RegToken(char.UTF8String): pass + +class SubsequentMessage(univ.Integer): + namedValues = namedval.NamedValues( + ('encrCert', 0), + ('challengeResp', 1) + ) + +class POPOPrivKey(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('thisMessage', univ.BitString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('subsequentMessage', SubsequentMessage().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('dhMAC', univ.BitString().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + +class PBMParameter(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('salt', univ.OctetString()), + namedtype.NamedType('owf', AlgorithmIdentifier()), + namedtype.NamedType('iterationCount', univ.Integer()), + namedtype.NamedType('mac', AlgorithmIdentifier()) + ) + +class PKMACValue(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('algId', AlgorithmIdentifier()), + namedtype.NamedType('value', univ.BitString()) + ) + +class POPOSigningKeyInput(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('authInfo', univ.Choice(componentType=namedtype.NamedTypes(namedtype.NamedType('sender', GeneralName().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), namedtype.NamedType('publicKeyMAC', PKMACValue())))), + namedtype.NamedType('publicKey', SubjectPublicKeyInfo()) + ) + +class POPOSigningKey(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('poposkInput', POPOSigningKeyInput().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))), + namedtype.NamedType('algorithmIdentifier', AlgorithmIdentifier()), + namedtype.NamedType('signature', univ.BitString()) + ) + +class ProofOfPossession(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('raVerified', univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('signature', POPOSigningKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.NamedType('keyEncipherment', POPOPrivKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))), + namedtype.NamedType('keyAgreement', POPOPrivKey().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))) + ) + +class Controls(univ.SequenceOf): + componentType = AttributeTypeAndValue() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +class OptionalValidity(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('notBefore', Time().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('notAfter', Time().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +class CertTemplate(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('version', Version().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('serialNumber', univ.Integer().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.OptionalNamedType('signingAlg', AlgorithmIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2))), + namedtype.OptionalNamedType('issuer', Name().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 3))), + namedtype.OptionalNamedType('validity', OptionalValidity().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 4))), + namedtype.OptionalNamedType('subject', Name().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 5))), + namedtype.OptionalNamedType('publicKey', SubjectPublicKeyInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 6))), + namedtype.OptionalNamedType('issuerUID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 7))), + namedtype.OptionalNamedType('subjectUID', UniqueIdentifier().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8))), + namedtype.OptionalNamedType('extensions', Extensions().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 9))) + ) + +class CertRequest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certReqId', univ.Integer()), + namedtype.NamedType('certTemplate', CertTemplate()), + namedtype.OptionalNamedType('controls', Controls()) + ) + +class CertReq(CertRequest): pass + +class CertReqMsg(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certReq', CertRequest()), + namedtype.OptionalNamedType('pop', ProofOfPossession()), + namedtype.OptionalNamedType('regInfo', univ.SequenceOf(componentType=AttributeTypeAndValue()).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, MAX))) + ) + +class CertReqMessages(univ.SequenceOf): + componentType = CertReqMsg() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) diff --git a/src/pyasn1_modules/rfc2560.py b/src/pyasn1_modules/rfc2560.py new file mode 100644 index 00000000..0be1091b --- /dev/null +++ b/src/pyasn1_modules/rfc2560.py @@ -0,0 +1,171 @@ +# +# OCSP request/response syntax +# +# Derived from a minimal OCSP library (RFC2560) code written by +# Bud P. Bruegger +# Copyright: Ancitel, S.p.a, Rome, Italy +# License: BSD +# + +# +# current limitations: +# * request and response works only for a single certificate +# * only some values are parsed out of the response +# * the request does't set a nonce nor signature +# * there is no signature validation of the response +# * dates are left as strings in GeneralizedTime format -- datetime.datetime +# would be nicer +# +from pyasn1.type import tag, namedtype, namedval, univ, constraint, useful +from pyasn1_modules import rfc2459 + +# Start of OCSP module definitions + +# This should be in directory Authentication Framework (X.509) module + +class CRLReason(univ.Enumerated): + namedValues = namedval.NamedValues( + ('unspecified', 0), + ('keyCompromise', 1), + ('cACompromise', 2), + ('affiliationChanged', 3), + ('superseded', 4), + ('cessationOfOperation', 5), + ('certificateHold', 6), + ('removeFromCRL', 8), + ('privilegeWithdrawn', 9), + ('aACompromise', 10) + ) + +# end of directory Authentication Framework (X.509) module + +# This should be in PKIX Certificate Extensions module + +class GeneralName(univ.OctetString): pass + +# end of PKIX Certificate Extensions module + +id_kp_OCSPSigning = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 3, 9)) +id_pkix_ocsp = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1)) +id_pkix_ocsp_basic = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1, 1)) +id_pkix_ocsp_nonce = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1, 2)) +id_pkix_ocsp_crl = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1, 3)) +id_pkix_ocsp_response = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1, 4)) +id_pkix_ocsp_nocheck = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1, 5)) +id_pkix_ocsp_archive_cutoff = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1, 6)) +id_pkix_ocsp_service_locator = univ.ObjectIdentifier((1, 3, 6, 1, 5, 5, 7, 48, 1, 7)) + +class AcceptableResponses(univ.SequenceOf): + componentType = univ.ObjectIdentifier() + +class ArchiveCutoff(useful.GeneralizedTime): pass + +class UnknownInfo(univ.Null): pass + +class RevokedInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('revocationTime', useful.GeneralizedTime()), + namedtype.OptionalNamedType('revocationReason', CRLReason().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + +class CertID(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('hashAlgorithm', rfc2459.AlgorithmIdentifier()), + namedtype.NamedType('issuerNameHash', univ.OctetString()), + namedtype.NamedType('issuerKeyHash', univ.OctetString()), + namedtype.NamedType('serialNumber', rfc2459.CertificateSerialNumber()) + ) + +class CertStatus(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('good', univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('revoked', RevokedInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('unknown', UnknownInfo().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + +class SingleResponse(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certID', CertID()), + namedtype.NamedType('certStatus', CertStatus()), + namedtype.NamedType('thisUpdate', useful.GeneralizedTime()), + namedtype.OptionalNamedType('nextUpdate', useful.GeneralizedTime().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('singleExtensions', rfc2459.Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +class KeyHash(univ.OctetString): pass + +class ResponderID(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('byName', rfc2459.Name().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('byKey', KeyHash().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + +class Version(univ.Integer): + namedValues = namedval.NamedValues(('v1', 0)) + +class ResponseData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', Version('v1').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.NamedType('responderID', ResponderID()), + namedtype.NamedType('producedAt', useful.GeneralizedTime()), + namedtype.NamedType('responses', univ.SequenceOf(SingleResponse())), + namedtype.OptionalNamedType('responseExtensions', rfc2459.Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))) + ) + +class BasicOCSPResponse(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsResponseData', ResponseData()), + namedtype.NamedType('signatureAlgorithm', rfc2459.AlgorithmIdentifier()), + namedtype.NamedType('signature', univ.BitString()), + namedtype.OptionalNamedType('certs', univ.SequenceOf(rfc2459.Certificate()).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + +class ResponseBytes(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('responseType', univ.ObjectIdentifier()), + namedtype.NamedType('response', univ.OctetString()) + ) + +class OCSPResponseStatus(univ.Enumerated): + namedValues = namedval.NamedValues( + ('successful', 0), + ('malformedRequest', 1), + ('internalError', 2), + ('tryLater', 3), + ('undefinedStatus', 4), # should never occur + ('sigRequired', 5), + ('unauthorized', 6) + ) + +class OCSPResponse(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('responseStatus', OCSPResponseStatus()), + namedtype.OptionalNamedType('responseBytes', ResponseBytes().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + +class Request(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('reqCert', CertID()), + namedtype.OptionalNamedType('singleRequestExtensions', rfc2459.Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + +class Signature(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('signatureAlgorithm', rfc2459.AlgorithmIdentifier()), + namedtype.NamedType('signature', univ.BitString()), + namedtype.OptionalNamedType('certs', univ.SequenceOf(rfc2459.Certificate()).subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) + +class TBSRequest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.DefaultedNamedType('version', Version('v1').subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('requestorName', GeneralName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1))), + namedtype.NamedType('requestList', univ.SequenceOf(Request())), + namedtype.OptionalNamedType('requestExtensions', rfc2459.Extensions().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))) + ) + +class OCSPRequest(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('tbsRequest', TBSRequest()), + namedtype.OptionalNamedType('optionalSignature', Signature().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))) + ) diff --git a/src/pyasn1_modules/rfc3412.py b/src/pyasn1_modules/rfc3412.py new file mode 100644 index 00000000..e80ce315 --- /dev/null +++ b/src/pyasn1_modules/rfc3412.py @@ -0,0 +1,38 @@ +# +# SNMPv3 message syntax +# +# ASN.1 source from: +# http://www.ietf.org/rfc/rfc3412.txt +# +from pyasn1.type import univ, namedtype, namedval, tag, constraint +from pyasn1_modules import rfc1905 + +class ScopedPDU(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('contextEngineId', univ.OctetString()), + namedtype.NamedType('contextName', univ.OctetString()), + namedtype.NamedType('data', rfc1905.PDUs()) + ) + +class ScopedPduData(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('plaintext', ScopedPDU()), + namedtype.NamedType('encryptedPDU', univ.OctetString()), + ) + +class HeaderData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('msgID', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))), + namedtype.NamedType('msgMaxSize', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(484, 2147483647))), + namedtype.NamedType('msgFlags', univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 1))), + namedtype.NamedType('msgSecurityModel', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(1, 2147483647))) + ) + +class SNMPv3Message(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('msgVersion', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))), + namedtype.NamedType('msgGlobalData', HeaderData()), + namedtype.NamedType('msgSecurityParameters', univ.OctetString()), + namedtype.NamedType('msgData', ScopedPduData()) + ) + diff --git a/src/pyasn1_modules/rfc3414.py b/src/pyasn1_modules/rfc3414.py new file mode 100644 index 00000000..580c88e9 --- /dev/null +++ b/src/pyasn1_modules/rfc3414.py @@ -0,0 +1,17 @@ +# +# SNMPv3 message syntax +# +# ASN.1 source from: +# http://www.ietf.org/rfc/rfc3414.txt +# +from pyasn1.type import univ, namedtype, namedval, tag, constraint + +class UsmSecurityParameters(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('msgAuthoritativeEngineID', univ.OctetString()), + namedtype.NamedType('msgAuthoritativeEngineBoots', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))), + namedtype.NamedType('msgAuthoritativeEngineTime', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))), + namedtype.NamedType('msgUserName', univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, 32))), + namedtype.NamedType('msgAuthenticationParameters', univ.OctetString()), + namedtype.NamedType('msgPrivacyParameters', univ.OctetString()) + ) diff --git a/src/pyasn1_modules/rfc3447.py b/src/pyasn1_modules/rfc3447.py new file mode 100644 index 00000000..96dea7fc --- /dev/null +++ b/src/pyasn1_modules/rfc3447.py @@ -0,0 +1,35 @@ +# +# PKCS#1 syntax +# +# ASN.1 source from: +# ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn +# +# Sample captures could be obtained with "openssl genrsa" command +# +from pyasn1_modules.rfc2437 import * + +class OtherPrimeInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('prime', univ.Integer()), + namedtype.NamedType('exponent', univ.Integer()), + namedtype.NamedType('coefficient', univ.Integer()) + ) + +class OtherPrimeInfos(univ.SequenceOf): + componentType = OtherPrimeInfo() + subtypeSpec = univ.SequenceOf.subtypeSpec + \ + constraint.ValueSizeConstraint(1, MAX) + +class RSAPrivateKey(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', univ.Integer(namedValues=namedval.NamedValues(('two-prime', 0), ('multi', 1)))), + namedtype.NamedType('modulus', univ.Integer()), + namedtype.NamedType('publicExponent', univ.Integer()), + namedtype.NamedType('privateExponent', univ.Integer()), + namedtype.NamedType('prime1', univ.Integer()), + namedtype.NamedType('prime2', univ.Integer()), + namedtype.NamedType('exponent1', univ.Integer()), + namedtype.NamedType('exponent2', univ.Integer()), + namedtype.NamedType('coefficient', univ.Integer()), + namedtype.OptionalNamedType('otherPrimeInfos', OtherPrimeInfos()) + ) diff --git a/src/pyasn1_modules/rfc4210.py b/src/pyasn1_modules/rfc4210.py new file mode 100644 index 00000000..c5775600 --- /dev/null +++ b/src/pyasn1_modules/rfc4210.py @@ -0,0 +1,720 @@ +# +# Certificate Management Protocol structures as per RFC4210 +# +# Based on Alex Railean's work +# +from pyasn1.type import tag,namedtype,namedval,univ,constraint,char,useful +from pyasn1_modules import rfc2459, rfc2511, rfc2314 + +MAX = 64 + +class KeyIdentifier(univ.OctetString): pass + +class CMPCertificate(rfc2459.Certificate): pass + +class OOBCert(CMPCertificate): pass + +class CertAnnContent(CMPCertificate): pass + +class PKIFreeText(univ.SequenceOf): + """ + PKIFreeText ::= SEQUENCE SIZE (1..MAX) OF UTF8String + """ + componentType = char.UTF8String() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +class PollRepContent(univ.SequenceOf): + """ + PollRepContent ::= SEQUENCE OF SEQUENCE { + certReqId INTEGER, + checkAfter INTEGER, -- time in seconds + reason PKIFreeText OPTIONAL + } + """ + class CertReq(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certReqId', univ.Integer()), + namedtype.NamedType('checkAfter', univ.Integer()), + namedtype.OptionalNamedType('reason', PKIFreeText()) + ) + componentType = CertReq() + +class PollReqContent(univ.SequenceOf): + """ + PollReqContent ::= SEQUENCE OF SEQUENCE { + certReqId INTEGER + } + + """ + class CertReq(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('certReqId', univ.Integer()) + ) + componentType = CertReq() + +class InfoTypeAndValue(univ.Sequence): + """ + InfoTypeAndValue ::= SEQUENCE { + infoType OBJECT IDENTIFIER, + infoValue ANY DEFINED BY infoType OPTIONAL + }""" + componentType = namedtype.NamedTypes( + namedtype.NamedType('infoType', univ.ObjectIdentifier()), + namedtype.OptionalNamedType('infoValue', univ.Any()) + ) + +class GenRepContent(univ.SequenceOf): + componentType = InfoTypeAndValue() + +class GenMsgContent(univ.SequenceOf): + componentType = InfoTypeAndValue() + +class PKIConfirmContent(univ.Null): pass + +class CRLAnnContent(univ.SequenceOf): + componentType = rfc2459.CertificateList() + +class CAKeyUpdAnnContent(univ.Sequence): + """ + CAKeyUpdAnnContent ::= SEQUENCE { + oldWithNew CMPCertificate, + newWithOld CMPCertificate, + newWithNew CMPCertificate + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('oldWithNew', CMPCertificate()), + namedtype.NamedType('newWithOld', CMPCertificate()), + namedtype.NamedType('newWithNew', CMPCertificate()) + ) + +class RevDetails(univ.Sequence): + """ + RevDetails ::= SEQUENCE { + certDetails CertTemplate, + crlEntryDetails Extensions OPTIONAL + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('certDetails', rfc2511.CertTemplate()), + namedtype.OptionalNamedType('crlEntryDetails', rfc2459.Extensions()) + ) + +class RevReqContent(univ.SequenceOf): + componentType = RevDetails() + +class CertOrEncCert(univ.Choice): + """ + CertOrEncCert ::= CHOICE { + certificate [0] CMPCertificate, + encryptedCert [1] EncryptedValue + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('certificate', CMPCertificate().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0) + ) + ), + namedtype.NamedType('encryptedCert', rfc2511.EncryptedValue().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) + ) + ) + +class CertifiedKeyPair(univ.Sequence): + """ + CertifiedKeyPair ::= SEQUENCE { + certOrEncCert CertOrEncCert, + privateKey [0] EncryptedValue OPTIONAL, + publicationInfo [1] PKIPublicationInfo OPTIONAL + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('certOrEncCert', CertOrEncCert()), + namedtype.OptionalNamedType('privateKey', rfc2511.EncryptedValue().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0) + ) + ), + namedtype.OptionalNamedType('publicationInfo', rfc2511.PKIPublicationInfo().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) + ) + ) + + +class POPODecKeyRespContent(univ.SequenceOf): + componentType = univ.Integer() + +class Challenge(univ.Sequence): + """ + Challenge ::= SEQUENCE { + owf AlgorithmIdentifier OPTIONAL, + witness OCTET STRING, + challenge OCTET STRING + } + """ + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('owf', rfc2459.AlgorithmIdentifier()), + namedtype.NamedType('witness', univ.OctetString()), + namedtype.NamedType('challenge', univ.OctetString()) + ) + +class PKIStatus(univ.Integer): + """ + PKIStatus ::= INTEGER { + accepted (0), + grantedWithMods (1), + rejection (2), + waiting (3), + revocationWarning (4), + revocationNotification (5), + keyUpdateWarning (6) + } + """ + namedValues = namedval.NamedValues( + ('accepted', 0), + ('grantedWithMods', 1), + ('rejection', 2), + ('waiting', 3), + ('revocationWarning', 4), + ('revocationNotification', 5), + ('keyUpdateWarning', 6) + ) + +class PKIFailureInfo(univ.BitString): + """ + PKIFailureInfo ::= BIT STRING { + badAlg (0), + badMessageCheck (1), + badRequest (2), + badTime (3), + badCertId (4), + badDataFormat (5), + wrongAuthority (6), + incorrectData (7), + missingTimeStamp (8), + badPOP (9), + certRevoked (10), + certConfirmed (11), + wrongIntegrity (12), + badRecipientNonce (13), + timeNotAvailable (14), + unacceptedPolicy (15), + unacceptedExtension (16), + addInfoNotAvailable (17), + badSenderNonce (18), + badCertTemplate (19), + signerNotTrusted (20), + transactionIdInUse (21), + unsupportedVersion (22), + notAuthorized (23), + systemUnavail (24), + systemFailure (25), + duplicateCertReq (26) + """ + namedValues = namedval.NamedValues( + ('badAlg', 0), + ('badMessageCheck', 1), + ('badRequest', 2), + ('badTime', 3), + ('badCertId', 4), + ('badDataFormat', 5), + ('wrongAuthority', 6), + ('incorrectData', 7), + ('missingTimeStamp', 8), + ('badPOP', 9), + ('certRevoked', 10), + ('certConfirmed', 11), + ('wrongIntegrity', 12), + ('badRecipientNonce', 13), + ('timeNotAvailable', 14), + ('unacceptedPolicy', 15), + ('unacceptedExtension', 16), + ('addInfoNotAvailable', 17), + ('badSenderNonce', 18), + ('badCertTemplate', 19), + ('signerNotTrusted', 20), + ('transactionIdInUse', 21), + ('unsupportedVersion', 22), + ('notAuthorized', 23), + ('systemUnavail', 24), + ('systemFailure', 25), + ('duplicateCertReq', 26) + ) + +class PKIStatusInfo(univ.Sequence): + """ + PKIStatusInfo ::= SEQUENCE { + status PKIStatus, + statusString PKIFreeText OPTIONAL, + failInfo PKIFailureInfo OPTIONAL + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('status', PKIStatus()), + namedtype.OptionalNamedType('statusString', PKIFreeText()), + namedtype.OptionalNamedType('failInfo', PKIFailureInfo()) + ) + +class ErrorMsgContent(univ.Sequence): + """ + ErrorMsgContent ::= SEQUENCE { + pKIStatusInfo PKIStatusInfo, + errorCode INTEGER OPTIONAL, + -- implementation-specific error codes + errorDetails PKIFreeText OPTIONAL + -- implementation-specific error details + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('pKIStatusInfo', PKIStatusInfo()), + namedtype.OptionalNamedType('errorCode', univ.Integer()), + namedtype.OptionalNamedType('errorDetails', PKIFreeText()) + ) + +class CertStatus(univ.Sequence): + """ + CertStatus ::= SEQUENCE { + certHash OCTET STRING, + certReqId INTEGER, + statusInfo PKIStatusInfo OPTIONAL + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('certHash', univ.OctetString()), + namedtype.NamedType('certReqId', univ.Integer()), + namedtype.OptionalNamedType('statusInfo', PKIStatusInfo()) + ) + +class CertConfirmContent(univ.SequenceOf): + componentType = CertStatus() + +class RevAnnContent(univ.Sequence): + """ + RevAnnContent ::= SEQUENCE { + status PKIStatus, + certId CertId, + willBeRevokedAt GeneralizedTime, + badSinceDate GeneralizedTime, + crlDetails Extensions OPTIONAL + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('status', PKIStatus()), + namedtype.NamedType('certId', rfc2511.CertId()), + namedtype.NamedType('willBeRevokedAt', useful.GeneralizedTime()), + namedtype.NamedType('badSinceDate', useful.GeneralizedTime()), + namedtype.OptionalNamedType('crlDetails', rfc2459.Extensions()) + ) + +class RevRepContent(univ.Sequence): + """ + RevRepContent ::= SEQUENCE { + status SEQUENCE SIZE (1..MAX) OF PKIStatusInfo, + revCerts [0] SEQUENCE SIZE (1..MAX) OF CertId + OPTIONAL, + crls [1] SEQUENCE SIZE (1..MAX) OF CertificateList + OPTIONAL + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('status', PKIStatusInfo()), + namedtype.OptionalNamedType('revCerts', univ.SequenceOf( + componentType=rfc2511.CertId() + ).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX), + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0) + ) + ), + namedtype.OptionalNamedType('crls', univ.SequenceOf( + componentType=rfc2459.CertificateList() + ).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX), + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) + ) + ) + +class KeyRecRepContent(univ.Sequence): + """ + KeyRecRepContent ::= SEQUENCE { + status PKIStatusInfo, + newSigCert [0] CMPCertificate OPTIONAL, + caCerts [1] SEQUENCE SIZE (1..MAX) OF + CMPCertificate OPTIONAL, + keyPairHist [2] SEQUENCE SIZE (1..MAX) OF + CertifiedKeyPair OPTIONAL + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('status', PKIStatusInfo()), + namedtype.OptionalNamedType('newSigCert', CMPCertificate().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0) + ) + ), + namedtype.OptionalNamedType('caCerts', univ.SequenceOf( + componentType=CMPCertificate() + ).subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1), + subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + ) + ), + namedtype.OptionalNamedType('keyPairHist', univ.SequenceOf( + componentType=CertifiedKeyPair() + ).subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 2), + subtypeSpec=constraint.ValueSizeConstraint(1, MAX) + ) + ) + ) + +class CertResponse(univ.Sequence): + """ + CertResponse ::= SEQUENCE { + certReqId INTEGER, + status PKIStatusInfo, + certifiedKeyPair CertifiedKeyPair OPTIONAL, + rspInfo OCTET STRING OPTIONAL + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('certReqId', univ.Integer()), + namedtype.NamedType('status', PKIStatusInfo()), + namedtype.OptionalNamedType('certifiedKeyPair', CertifiedKeyPair()), + namedtype.OptionalNamedType('rspInfo', univ.OctetString()) + ) + +class CertRepMessage(univ.Sequence): + """ + CertRepMessage ::= SEQUENCE { + caPubs [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate + OPTIONAL, + response SEQUENCE OF CertResponse + } + """ + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('caPubs', univ.SequenceOf( + componentType=CMPCertificate() + ).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX), + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,1) + ) + ), + namedtype.NamedType('response', univ.SequenceOf( + componentType=CertResponse()) + ) + ) + +class POPODecKeyChallContent(univ.SequenceOf): + componentType = Challenge() + +class OOBCertHash(univ.Sequence): + """ + OOBCertHash ::= SEQUENCE { + hashAlg [0] AlgorithmIdentifier OPTIONAL, + certId [1] CertId OPTIONAL, + hashVal BIT STRING + } + """ + componentType = namedtype.NamedTypes( + namedtype.OptionalNamedType('hashAlg', + rfc2459.AlgorithmIdentifier().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,0) + ) + ), + namedtype.OptionalNamedType('certId', rfc2511.CertId().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,1) + ) + ), + namedtype.NamedType('hashVal', univ.BitString()) + ) + +# pyasn1 does not naturally handle recursive definitions, thus this hack: +# NestedMessageContent ::= PKIMessages +class NestedMessageContent(univ.SequenceOf): + """ + NestedMessageContent ::= PKIMessages + """ + componentType = univ.Any() + +class DHBMParameter(univ.Sequence): + """ + DHBMParameter ::= SEQUENCE { + owf AlgorithmIdentifier, + -- AlgId for a One-Way Function (SHA-1 recommended) + mac AlgorithmIdentifier + -- the MAC AlgId (e.g., DES-MAC, Triple-DES-MAC [PKCS11], + } -- or HMAC [RFC2104, RFC2202]) + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('owf', rfc2459.AlgorithmIdentifier()), + namedtype.NamedType('mac', rfc2459.AlgorithmIdentifier()) + ) + +id_DHBasedMac = univ.ObjectIdentifier('1.2.840.113533.7.66.30') + +class PBMParameter(univ.Sequence): + """ + PBMParameter ::= SEQUENCE { + salt OCTET STRING, + owf AlgorithmIdentifier, + iterationCount INTEGER, + mac AlgorithmIdentifier + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('salt', univ.OctetString().subtype( + subtypeSpec=constraint.ValueSizeConstraint(0, 128) + ) + ), + namedtype.NamedType('owf', rfc2459.AlgorithmIdentifier()), + namedtype.NamedType('iterationCount', univ.Integer()), + namedtype.NamedType('mac', rfc2459.AlgorithmIdentifier()) + ) + +id_PasswordBasedMac = univ.ObjectIdentifier('1.2.840.113533.7.66.13') + +class PKIProtection(univ.BitString): pass + +# pyasn1 does not naturally handle recursive definitions, thus this hack: +# NestedMessageContent ::= PKIMessages +nestedMessageContent = NestedMessageContent().subtype(explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,20)) + +class PKIBody(univ.Choice): + """ + PKIBody ::= CHOICE { -- message-specific body elements + ir [0] CertReqMessages, --Initialization Request + ip [1] CertRepMessage, --Initialization Response + cr [2] CertReqMessages, --Certification Request + cp [3] CertRepMessage, --Certification Response + p10cr [4] CertificationRequest, --imported from [PKCS10] + popdecc [5] POPODecKeyChallContent, --pop Challenge + popdecr [6] POPODecKeyRespContent, --pop Response + kur [7] CertReqMessages, --Key Update Request + kup [8] CertRepMessage, --Key Update Response + krr [9] CertReqMessages, --Key Recovery Request + krp [10] KeyRecRepContent, --Key Recovery Response + rr [11] RevReqContent, --Revocation Request + rp [12] RevRepContent, --Revocation Response + ccr [13] CertReqMessages, --Cross-Cert. Request + ccp [14] CertRepMessage, --Cross-Cert. Response + ckuann [15] CAKeyUpdAnnContent, --CA Key Update Ann. + cann [16] CertAnnContent, --Certificate Ann. + rann [17] RevAnnContent, --Revocation Ann. + crlann [18] CRLAnnContent, --CRL Announcement + pkiconf [19] PKIConfirmContent, --Confirmation + nested [20] NestedMessageContent, --Nested Message + genm [21] GenMsgContent, --General Message + genp [22] GenRepContent, --General Response + error [23] ErrorMsgContent, --Error Message + certConf [24] CertConfirmContent, --Certificate confirm + pollReq [25] PollReqContent, --Polling request + pollRep [26] PollRepContent --Polling response + + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('ir', rfc2511.CertReqMessages().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,0) + ) + ), + namedtype.NamedType('ip', CertRepMessage().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,1) + ) + ), + namedtype.NamedType('cr', rfc2511.CertReqMessages().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,2) + ) + ), + namedtype.NamedType('cp', CertRepMessage().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,3) + ) + ), + namedtype.NamedType('p10cr', rfc2314.CertificationRequest().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,4) + ) + ), + namedtype.NamedType('popdecc', POPODecKeyChallContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,5) + ) + ), + namedtype.NamedType('popdecr', POPODecKeyRespContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,6) + ) + ), + namedtype.NamedType('kur', rfc2511.CertReqMessages().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,7) + ) + ), + namedtype.NamedType('kup', CertRepMessage().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,8) + ) + ), + namedtype.NamedType('krr', rfc2511.CertReqMessages().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,9) + ) + ), + namedtype.NamedType('krp', KeyRecRepContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,10) + ) + ), + namedtype.NamedType('rr', RevReqContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,11) + ) + ), + namedtype.NamedType('rp', RevRepContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,12) + ) + ), + namedtype.NamedType('ccr', rfc2511.CertReqMessages().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,13) + ) + ), + namedtype.NamedType('ccp', CertRepMessage().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,14) + ) + ), + namedtype.NamedType('ckuann', CAKeyUpdAnnContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,15) + ) + ), + namedtype.NamedType('cann', CertAnnContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,16) + ) + ), + namedtype.NamedType('rann', RevAnnContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,17) + ) + ), + namedtype.NamedType('crlann', CRLAnnContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,18) + ) + ), + namedtype.NamedType('pkiconf', PKIConfirmContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,19) + ) + ), + namedtype.NamedType('nested', nestedMessageContent), +# namedtype.NamedType('nested', NestedMessageContent().subtype( +# explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,20) +# ) +# ), + namedtype.NamedType('genm', GenMsgContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,21) + ) + ), + namedtype.NamedType('gen', GenRepContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,22) + ) + ), + namedtype.NamedType('error', ErrorMsgContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,23) + ) + ), + namedtype.NamedType('certConf', CertConfirmContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,24) + ) + ), + namedtype.NamedType('pollReq', PollReqContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,25) + ) + ), + namedtype.NamedType('pollRep', PollRepContent().subtype( + explicitTag=tag.Tag(tag.tagClassContext,tag.tagFormatConstructed,26) + ) + ) + ) + + +class PKIHeader(univ.Sequence): + """ + PKIHeader ::= SEQUENCE { + pvno INTEGER { cmp1999(1), cmp2000(2) }, + sender GeneralName, + recipient GeneralName, + messageTime [0] GeneralizedTime OPTIONAL, + protectionAlg [1] AlgorithmIdentifier OPTIONAL, + senderKID [2] KeyIdentifier OPTIONAL, + recipKID [3] KeyIdentifier OPTIONAL, + transactionID [4] OCTET STRING OPTIONAL, + senderNonce [5] OCTET STRING OPTIONAL, + recipNonce [6] OCTET STRING OPTIONAL, + freeText [7] PKIFreeText OPTIONAL, + generalInfo [8] SEQUENCE SIZE (1..MAX) OF + InfoTypeAndValue OPTIONAL + } + + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('pvno', univ.Integer( + namedValues=namedval.NamedValues( + ('cmp1999', 1), + ('cmp2000', 2) + ) + ) + ), + namedtype.NamedType('sender', rfc2459.GeneralName()), + namedtype.NamedType('recipient', rfc2459.GeneralName()), + namedtype.OptionalNamedType('messageTime', useful.GeneralizedTime().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('protectionAlg', rfc2459.AlgorithmIdentifier().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))), + namedtype.OptionalNamedType('senderKID', rfc2459.KeyIdentifier().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2))), + namedtype.OptionalNamedType('recipKID', rfc2459.KeyIdentifier().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 3))), + namedtype.OptionalNamedType('transactionID', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 4))), + namedtype.OptionalNamedType('senderNonce', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 5))), + namedtype.OptionalNamedType('recipNonce', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 6))), + namedtype.OptionalNamedType('freeText', PKIFreeText().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 7))), + namedtype.OptionalNamedType('generalInfo', + univ.SequenceOf( + componentType=InfoTypeAndValue().subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX), + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 8) + ) + ) + ) + ) + +class ProtectedPart(univ.Sequence): + """ + ProtectedPart ::= SEQUENCE { + header PKIHeader, + body PKIBody + } + """ + componentType = namedtype.NamedTypes( + namedtype.NamedType('header', PKIHeader()), + namedtype.NamedType('infoValue', PKIBody()) + ) + +class PKIMessage(univ.Sequence): + """ + PKIMessage ::= SEQUENCE { + header PKIHeader, + body PKIBody, + protection [0] PKIProtection OPTIONAL, + extraCerts [1] SEQUENCE SIZE (1..MAX) OF CMPCertificate + OPTIONAL + }""" + componentType = namedtype.NamedTypes( + namedtype.NamedType('header', PKIHeader()), + namedtype.NamedType('body', PKIBody()), + namedtype.OptionalNamedType('protection', PKIProtection().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType( 'extraCerts', + univ.SequenceOf( + componentType=CMPCertificate() + ).subtype( + subtypeSpec=constraint.ValueSizeConstraint(1, MAX), + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1) + ) + ) + ) + +class PKIMessages(univ.SequenceOf): + """ + PKIMessages ::= SEQUENCE SIZE (1..MAX) OF PKIMessage + """ + componentType = PKIMessage() + subtypeSpec = univ.SequenceOf.subtypeSpec + constraint.ValueSizeConstraint(1, MAX) + +# pyasn1 does not naturally handle recursive definitions, thus this hack: +# NestedMessageContent ::= PKIMessages +NestedMessageContent.componentType = PKIMessages() +nestedMessageContent.componentType = PKIMessages() diff --git a/src/pyasn1_modules/rfc5208.py b/src/pyasn1_modules/rfc5208.py new file mode 100644 index 00000000..d1d2c169 --- /dev/null +++ b/src/pyasn1_modules/rfc5208.py @@ -0,0 +1,39 @@ +# +# PKCS#8 syntax +# +# ASN.1 source from: +# http://tools.ietf.org/html/rfc5208 +# +# Sample captures could be obtained with "openssl pkcs8 -topk8" command +# +from pyasn1.type import tag, namedtype, namedval, univ, constraint +from pyasn1_modules.rfc2459 import * +from pyasn1_modules import rfc2251 + +class KeyEncryptionAlgorithms(AlgorithmIdentifier): pass + +class PrivateKeyAlgorithms(AlgorithmIdentifier): pass + +class EncryptedData(univ.OctetString): pass + +class EncryptedPrivateKeyInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('encryptionAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('encryptedData', EncryptedData()) + ) + +class PrivateKey(univ.OctetString): pass + +class Attributes(univ.SetOf): + componentType = rfc2251.Attribute() + +class Version(univ.Integer): + namedValues = namedval.NamedValues(('v1', 0), ('v2', 1)) + +class PrivateKeyInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', Version()), + namedtype.NamedType('privateKeyAlgorithm', AlgorithmIdentifier()), + namedtype.NamedType('privateKey', PrivateKey()), + namedtype.OptionalNamedType('attributes', Attributes().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))) + ) diff --git a/src/rsa/__init__.py b/src/rsa/__init__.py new file mode 100644 index 00000000..c4654e99 --- /dev/null +++ b/src/rsa/__init__.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""RSA module + +Module for calculating large primes, and RSA encryption, decryption, signing +and verification. Includes generating public and private keys. + +WARNING: this implementation does not use random padding, compression of the +cleartext input to prevent repetitions, or other common security improvements. +Use with care. + +If you want to have a more secure implementation, use the functions from the +``rsa.pkcs1`` module. + +""" + +__author__ = "Sybren Stuvel, Barry Mead and Yesudeep Mangalapilly" +__date__ = "2016-01-13" +__version__ = '3.3' + +from rsa.key import newkeys, PrivateKey, PublicKey +from rsa.pkcs1 import encrypt, decrypt, sign, verify, DecryptionError, \ + VerificationError + +# Do doctest if we're run directly +if __name__ == "__main__": + import doctest + doctest.testmod() + +__all__ = ["newkeys", "encrypt", "decrypt", "sign", "verify", 'PublicKey', + 'PrivateKey', 'DecryptionError', 'VerificationError'] + diff --git a/src/rsa/_compat.py b/src/rsa/_compat.py new file mode 100644 index 00000000..3c4eb81b --- /dev/null +++ b/src/rsa/_compat.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Python compatibility wrappers.""" + + +from __future__ import absolute_import + +import sys +from struct import pack + +try: + MAX_INT = sys.maxsize +except AttributeError: + MAX_INT = sys.maxint + +MAX_INT64 = (1 << 63) - 1 +MAX_INT32 = (1 << 31) - 1 +MAX_INT16 = (1 << 15) - 1 + +# Determine the word size of the processor. +if MAX_INT == MAX_INT64: + # 64-bit processor. + MACHINE_WORD_SIZE = 64 +elif MAX_INT == MAX_INT32: + # 32-bit processor. + MACHINE_WORD_SIZE = 32 +else: + # Else we just assume 64-bit processor keeping up with modern times. + MACHINE_WORD_SIZE = 64 + + +try: + # < Python3 + unicode_type = unicode + have_python3 = False +except NameError: + # Python3. + unicode_type = str + have_python3 = True + +# Fake byte literals. +if str is unicode_type: + def byte_literal(s): + return s.encode('latin1') +else: + def byte_literal(s): + return s + +# ``long`` is no more. Do type detection using this instead. +try: + integer_types = (int, long) +except NameError: + integer_types = (int,) + +b = byte_literal + +try: + # Python 2.6 or higher. + bytes_type = bytes +except NameError: + # Python 2.5 + bytes_type = str + + +# To avoid calling b() multiple times in tight loops. +ZERO_BYTE = b('\x00') +EMPTY_BYTE = b('') + + +def is_bytes(obj): + """ + Determines whether the given value is a byte string. + + :param obj: + The value to test. + :returns: + ``True`` if ``value`` is a byte string; ``False`` otherwise. + """ + return isinstance(obj, bytes_type) + + +def is_integer(obj): + """ + Determines whether the given value is an integer. + + :param obj: + The value to test. + :returns: + ``True`` if ``value`` is an integer; ``False`` otherwise. + """ + return isinstance(obj, integer_types) + + +def byte(num): + """ + Converts a number between 0 and 255 (both inclusive) to a base-256 (byte) + representation. + + Use it as a replacement for ``chr`` where you are expecting a byte + because this will work on all current versions of Python:: + + :param num: + An unsigned integer between 0 and 255 (both inclusive). + :returns: + A single byte. + """ + return pack("B", num) + + +def get_word_alignment(num, force_arch=64, + _machine_word_size=MACHINE_WORD_SIZE): + """ + Returns alignment details for the given number based on the platform + Python is running on. + + :param num: + Unsigned integral number. + :param force_arch: + If you don't want to use 64-bit unsigned chunks, set this to + anything other than 64. 32-bit chunks will be preferred then. + Default 64 will be used when on a 64-bit machine. + :param _machine_word_size: + (Internal) The machine word size used for alignment. + :returns: + 4-tuple:: + + (word_bits, word_bytes, + max_uint, packing_format_type) + """ + max_uint64 = 0xffffffffffffffff + max_uint32 = 0xffffffff + max_uint16 = 0xffff + max_uint8 = 0xff + + if force_arch == 64 and _machine_word_size >= 64 and num > max_uint32: + # 64-bit unsigned integer. + return 64, 8, max_uint64, "Q" + elif num > max_uint16: + # 32-bit unsigned integer + return 32, 4, max_uint32, "L" + elif num > max_uint8: + # 16-bit unsigned integer. + return 16, 2, max_uint16, "H" + else: + # 8-bit unsigned integer. + return 8, 1, max_uint8, "B" diff --git a/src/rsa/_version133.py b/src/rsa/_version133.py new file mode 100644 index 00000000..dff0dda8 --- /dev/null +++ b/src/rsa/_version133.py @@ -0,0 +1,458 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""RSA module +pri = k[1] //Private part of keys d,p,q + +Module for calculating large primes, and RSA encryption, decryption, +signing and verification. Includes generating public and private keys. + +WARNING: this code implements the mathematics of RSA. It is not suitable for +real-world secure cryptography purposes. It has not been reviewed by a security +expert. It does not include padding of data. There are many ways in which the +output of this module, when used without any modification, can be sucessfully +attacked. +""" + +__author__ = "Sybren Stuvel, Marloes de Boer and Ivo Tamboer" +__date__ = "2010-02-05" +__version__ = '1.3.3' + +# NOTE: Python's modulo can return negative numbers. We compensate for +# this behaviour using the abs() function + +from cPickle import dumps, loads +import base64 +import math +import os +import random +import sys +import types +import zlib + +from rsa._compat import byte + +# Display a warning that this insecure version is imported. +import warnings +warnings.warn('Insecure version of the RSA module is imported as %s, be careful' + % __name__) + +def gcd(p, q): + """Returns the greatest common divisor of p and q + + + >>> gcd(42, 6) + 6 + """ + if p>> (128*256 + 64)*256 + + 15 + 8405007 + >>> l = [128, 64, 15] + >>> bytes2int(l) + 8405007 + """ + + if not (type(bytes) is types.ListType or type(bytes) is types.StringType): + raise TypeError("You must pass a string or a list") + + # Convert byte stream to integer + integer = 0 + for byte in bytes: + integer *= 256 + if type(byte) is types.StringType: byte = ord(byte) + integer += byte + + return integer + +def int2bytes(number): + """Converts a number to a string of bytes + + >>> bytes2int(int2bytes(123456789)) + 123456789 + """ + + if not (type(number) is types.LongType or type(number) is types.IntType): + raise TypeError("You must pass a long or an int") + + string = "" + + while number > 0: + string = "%s%s" % (byte(number & 0xFF), string) + number /= 256 + + return string + +def fast_exponentiation(a, p, n): + """Calculates r = a^p mod n + """ + result = a % n + remainders = [] + while p != 1: + remainders.append(p & 1) + p = p >> 1 + while remainders: + rem = remainders.pop() + result = ((a ** rem) * result ** 2) % n + return result + +def read_random_int(nbits): + """Reads a random integer of approximately nbits bits rounded up + to whole bytes""" + + nbytes = ceil(nbits/8.) + randomdata = os.urandom(nbytes) + return bytes2int(randomdata) + +def ceil(x): + """ceil(x) -> int(math.ceil(x))""" + + return int(math.ceil(x)) + +def randint(minvalue, maxvalue): + """Returns a random integer x with minvalue <= x <= maxvalue""" + + # Safety - get a lot of random data even if the range is fairly + # small + min_nbits = 32 + + # The range of the random numbers we need to generate + range = maxvalue - minvalue + + # Which is this number of bytes + rangebytes = ceil(math.log(range, 2) / 8.) + + # Convert to bits, but make sure it's always at least min_nbits*2 + rangebits = max(rangebytes * 8, min_nbits * 2) + + # Take a random number of bits between min_nbits and rangebits + nbits = random.randint(min_nbits, rangebits) + + return (read_random_int(nbits) % range) + minvalue + +def fermat_little_theorem(p): + """Returns 1 if p may be prime, and something else if p definitely + is not prime""" + + a = randint(1, p-1) + return fast_exponentiation(a, p-1, p) + +def jacobi(a, b): + """Calculates the value of the Jacobi symbol (a/b) + """ + + if a % b == 0: + return 0 + result = 1 + while a > 1: + if a & 1: + if ((a-1)*(b-1) >> 2) & 1: + result = -result + b, a = a, b % a + else: + if ((b ** 2 - 1) >> 3) & 1: + result = -result + a = a >> 1 + return result + +def jacobi_witness(x, n): + """Returns False if n is an Euler pseudo-prime with base x, and + True otherwise. + """ + + j = jacobi(x, n) % n + f = fast_exponentiation(x, (n-1)/2, n) + + if j == f: return False + return True + +def randomized_primality_testing(n, k): + """Calculates whether n is composite (which is always correct) or + prime (which is incorrect with error probability 2**-k) + + Returns False if the number if composite, and True if it's + probably prime. + """ + + q = 0.5 # Property of the jacobi_witness function + + # t = int(math.ceil(k / math.log(1/q, 2))) + t = ceil(k / math.log(1/q, 2)) + for i in range(t+1): + x = randint(1, n-1) + if jacobi_witness(x, n): return False + + return True + +def is_prime(number): + """Returns True if the number is prime, and False otherwise. + + >>> is_prime(42) + 0 + >>> is_prime(41) + 1 + """ + + """ + if not fermat_little_theorem(number) == 1: + # Not prime, according to Fermat's little theorem + return False + """ + + if randomized_primality_testing(number, 5): + # Prime, according to Jacobi + return True + + # Not prime + return False + + +def getprime(nbits): + """Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In + other words: nbits is rounded up to whole bytes. + + >>> p = getprime(8) + >>> is_prime(p-1) + 0 + >>> is_prime(p) + 1 + >>> is_prime(p+1) + 0 + """ + + nbytes = int(math.ceil(nbits/8.)) + + while True: + integer = read_random_int(nbits) + + # Make sure it's odd + integer |= 1 + + # Test for primeness + if is_prime(integer): break + + # Retry if not prime + + return integer + +def are_relatively_prime(a, b): + """Returns True if a and b are relatively prime, and False if they + are not. + + >>> are_relatively_prime(2, 3) + 1 + >>> are_relatively_prime(2, 4) + 0 + """ + + d = gcd(a, b) + return (d == 1) + +def find_p_q(nbits): + """Returns a tuple of two different primes of nbits bits""" + + p = getprime(nbits) + while True: + q = getprime(nbits) + if not q == p: break + + return (p, q) + +def extended_euclid_gcd(a, b): + """Returns a tuple (d, i, j) such that d = gcd(a, b) = ia + jb + """ + + if b == 0: + return (a, 1, 0) + + q = abs(a % b) + r = long(a / b) + (d, k, l) = extended_euclid_gcd(b, q) + + return (d, l, k - l*r) + +# Main function: calculate encryption and decryption keys +def calculate_keys(p, q, nbits): + """Calculates an encryption and a decryption key for p and q, and + returns them as a tuple (e, d)""" + + n = p * q + phi_n = (p-1) * (q-1) + + while True: + # Make sure e has enough bits so we ensure "wrapping" through + # modulo n + e = getprime(max(8, nbits/2)) + if are_relatively_prime(e, n) and are_relatively_prime(e, phi_n): break + + (d, i, j) = extended_euclid_gcd(e, phi_n) + + if not d == 1: + raise Exception("e (%d) and phi_n (%d) are not relatively prime" % (e, phi_n)) + + if not (e * i) % phi_n == 1: + raise Exception("e (%d) and i (%d) are not mult. inv. modulo phi_n (%d)" % (e, i, phi_n)) + + return (e, i) + + +def gen_keys(nbits): + """Generate RSA keys of nbits bits. Returns (p, q, e, d). + + Note: this can take a long time, depending on the key size. + """ + + while True: + (p, q) = find_p_q(nbits) + (e, d) = calculate_keys(p, q, nbits) + + # For some reason, d is sometimes negative. We don't know how + # to fix it (yet), so we keep trying until everything is shiny + if d > 0: break + + return (p, q, e, d) + +def gen_pubpriv_keys(nbits): + """Generates public and private keys, and returns them as (pub, + priv). + + The public key consists of a dict {e: ..., , n: ....). The private + key consists of a dict {d: ...., p: ...., q: ....). + """ + + (p, q, e, d) = gen_keys(nbits) + + return ( {'e': e, 'n': p*q}, {'d': d, 'p': p, 'q': q} ) + +def encrypt_int(message, ekey, n): + """Encrypts a message using encryption key 'ekey', working modulo + n""" + + if type(message) is types.IntType: + return encrypt_int(long(message), ekey, n) + + if not type(message) is types.LongType: + raise TypeError("You must pass a long or an int") + + if message > 0 and \ + math.floor(math.log(message, 2)) > math.floor(math.log(n, 2)): + raise OverflowError("The message is too long") + + return fast_exponentiation(message, ekey, n) + +def decrypt_int(cyphertext, dkey, n): + """Decrypts a cypher text using the decryption key 'dkey', working + modulo n""" + + return encrypt_int(cyphertext, dkey, n) + +def sign_int(message, dkey, n): + """Signs 'message' using key 'dkey', working modulo n""" + + return decrypt_int(message, dkey, n) + +def verify_int(signed, ekey, n): + """verifies 'signed' using key 'ekey', working modulo n""" + + return encrypt_int(signed, ekey, n) + +def picklechops(chops): + """Pickles and base64encodes it's argument chops""" + + value = zlib.compress(dumps(chops)) + encoded = base64.encodestring(value) + return encoded.strip() + +def unpicklechops(string): + """base64decodes and unpickes it's argument string into chops""" + + return loads(zlib.decompress(base64.decodestring(string))) + +def chopstring(message, key, n, funcref): + """Splits 'message' into chops that are at most as long as n, + converts these into integers, and calls funcref(integer, key, n) + for each chop. + + Used by 'encrypt' and 'sign'. + """ + + msglen = len(message) + mbits = msglen * 8 + nbits = int(math.floor(math.log(n, 2))) + nbytes = nbits / 8 + blocks = msglen / nbytes + + if msglen % nbytes > 0: + blocks += 1 + + cypher = [] + + for bindex in range(blocks): + offset = bindex * nbytes + block = message[offset:offset+nbytes] + value = bytes2int(block) + cypher.append(funcref(value, key, n)) + + return picklechops(cypher) + +def gluechops(chops, key, n, funcref): + """Glues chops back together into a string. calls + funcref(integer, key, n) for each chop. + + Used by 'decrypt' and 'verify'. + """ + message = "" + + chops = unpicklechops(chops) + + for cpart in chops: + mpart = funcref(cpart, key, n) + message += int2bytes(mpart) + + return message + +def encrypt(message, key): + """Encrypts a string 'message' with the public key 'key'""" + + return chopstring(message, key['e'], key['n'], encrypt_int) + +def sign(message, key): + """Signs a string 'message' with the private key 'key'""" + + return chopstring(message, key['d'], key['p']*key['q'], decrypt_int) + +def decrypt(cypher, key): + """Decrypts a cypher with the private key 'key'""" + + return gluechops(cypher, key['d'], key['p']*key['q'], decrypt_int) + +def verify(cypher, key): + """Verifies a cypher with the public key 'key'""" + + return gluechops(cypher, key['e'], key['n'], encrypt_int) + +# Do doctest if we're not imported +if __name__ == "__main__": + import doctest + doctest.testmod() + +__all__ = ["gen_pubpriv_keys", "encrypt", "decrypt", "sign", "verify"] + diff --git a/src/rsa/_version200.py b/src/rsa/_version200.py new file mode 100644 index 00000000..28f36018 --- /dev/null +++ b/src/rsa/_version200.py @@ -0,0 +1,545 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""RSA module + +Module for calculating large primes, and RSA encryption, decryption, +signing and verification. Includes generating public and private keys. + +WARNING: this implementation does not use random padding, compression of the +cleartext input to prevent repetitions, or other common security improvements. +Use with care. + +""" + +__author__ = "Sybren Stuvel, Marloes de Boer, Ivo Tamboer, and Barry Mead" +__date__ = "2010-02-08" +__version__ = '2.0' + +import math +import os +import random +import sys +import types +from rsa._compat import byte + +# Display a warning that this insecure version is imported. +import warnings +warnings.warn('Insecure version of the RSA module is imported as %s' % __name__) + + +def bit_size(number): + """Returns the number of bits required to hold a specific long number""" + + return int(math.ceil(math.log(number,2))) + +def gcd(p, q): + """Returns the greatest common divisor of p and q + >>> gcd(48, 180) + 12 + """ + # Iterateive Version is faster and uses much less stack space + while q != 0: + if p < q: (p,q) = (q,p) + (p,q) = (q, p % q) + return p + + +def bytes2int(bytes): + """Converts a list of bytes or a string to an integer + + >>> (((128 * 256) + 64) * 256) + 15 + 8405007 + >>> l = [128, 64, 15] + >>> bytes2int(l) #same as bytes2int('\x80@\x0f') + 8405007 + """ + + if not (type(bytes) is types.ListType or type(bytes) is types.StringType): + raise TypeError("You must pass a string or a list") + + # Convert byte stream to integer + integer = 0 + for byte in bytes: + integer *= 256 + if type(byte) is types.StringType: byte = ord(byte) + integer += byte + + return integer + +def int2bytes(number): + """ + Converts a number to a string of bytes + """ + + if not (type(number) is types.LongType or type(number) is types.IntType): + raise TypeError("You must pass a long or an int") + + string = "" + + while number > 0: + string = "%s%s" % (byte(number & 0xFF), string) + number /= 256 + + return string + +def to64(number): + """Converts a number in the range of 0 to 63 into base 64 digit + character in the range of '0'-'9', 'A'-'Z', 'a'-'z','-','_'. + + >>> to64(10) + 'A' + """ + + if not (type(number) is types.LongType or type(number) is types.IntType): + raise TypeError("You must pass a long or an int") + + if 0 <= number <= 9: #00-09 translates to '0' - '9' + return byte(number + 48) + + if 10 <= number <= 35: + return byte(number + 55) #10-35 translates to 'A' - 'Z' + + if 36 <= number <= 61: + return byte(number + 61) #36-61 translates to 'a' - 'z' + + if number == 62: # 62 translates to '-' (minus) + return byte(45) + + if number == 63: # 63 translates to '_' (underscore) + return byte(95) + + raise ValueError('Invalid Base64 value: %i' % number) + + +def from64(number): + """Converts an ordinal character value in the range of + 0-9,A-Z,a-z,-,_ to a number in the range of 0-63. + + >>> from64(49) + 1 + """ + + if not (type(number) is types.LongType or type(number) is types.IntType): + raise TypeError("You must pass a long or an int") + + if 48 <= number <= 57: #ord('0') - ord('9') translates to 0-9 + return(number - 48) + + if 65 <= number <= 90: #ord('A') - ord('Z') translates to 10-35 + return(number - 55) + + if 97 <= number <= 122: #ord('a') - ord('z') translates to 36-61 + return(number - 61) + + if number == 45: #ord('-') translates to 62 + return(62) + + if number == 95: #ord('_') translates to 63 + return(63) + + raise ValueError('Invalid Base64 value: %i' % number) + + +def int2str64(number): + """Converts a number to a string of base64 encoded characters in + the range of '0'-'9','A'-'Z,'a'-'z','-','_'. + + >>> int2str64(123456789) + '7MyqL' + """ + + if not (type(number) is types.LongType or type(number) is types.IntType): + raise TypeError("You must pass a long or an int") + + string = "" + + while number > 0: + string = "%s%s" % (to64(number & 0x3F), string) + number /= 64 + + return string + + +def str642int(string): + """Converts a base64 encoded string into an integer. + The chars of this string in in the range '0'-'9','A'-'Z','a'-'z','-','_' + + >>> str642int('7MyqL') + 123456789 + """ + + if not (type(string) is types.ListType or type(string) is types.StringType): + raise TypeError("You must pass a string or a list") + + integer = 0 + for byte in string: + integer *= 64 + if type(byte) is types.StringType: byte = ord(byte) + integer += from64(byte) + + return integer + +def read_random_int(nbits): + """Reads a random integer of approximately nbits bits rounded up + to whole bytes""" + + nbytes = int(math.ceil(nbits/8.)) + randomdata = os.urandom(nbytes) + return bytes2int(randomdata) + +def randint(minvalue, maxvalue): + """Returns a random integer x with minvalue <= x <= maxvalue""" + + # Safety - get a lot of random data even if the range is fairly + # small + min_nbits = 32 + + # The range of the random numbers we need to generate + range = (maxvalue - minvalue) + 1 + + # Which is this number of bytes + rangebytes = ((bit_size(range) + 7) / 8) + + # Convert to bits, but make sure it's always at least min_nbits*2 + rangebits = max(rangebytes * 8, min_nbits * 2) + + # Take a random number of bits between min_nbits and rangebits + nbits = random.randint(min_nbits, rangebits) + + return (read_random_int(nbits) % range) + minvalue + +def jacobi(a, b): + """Calculates the value of the Jacobi symbol (a/b) + where both a and b are positive integers, and b is odd + """ + + if a == 0: return 0 + result = 1 + while a > 1: + if a & 1: + if ((a-1)*(b-1) >> 2) & 1: + result = -result + a, b = b % a, a + else: + if (((b * b) - 1) >> 3) & 1: + result = -result + a >>= 1 + if a == 0: return 0 + return result + +def jacobi_witness(x, n): + """Returns False if n is an Euler pseudo-prime with base x, and + True otherwise. + """ + + j = jacobi(x, n) % n + f = pow(x, (n-1)/2, n) + + if j == f: return False + return True + +def randomized_primality_testing(n, k): + """Calculates whether n is composite (which is always correct) or + prime (which is incorrect with error probability 2**-k) + + Returns False if the number is composite, and True if it's + probably prime. + """ + + # 50% of Jacobi-witnesses can report compositness of non-prime numbers + + for i in range(k): + x = randint(1, n-1) + if jacobi_witness(x, n): return False + + return True + +def is_prime(number): + """Returns True if the number is prime, and False otherwise. + + >>> is_prime(42) + 0 + >>> is_prime(41) + 1 + """ + + if randomized_primality_testing(number, 6): + # Prime, according to Jacobi + return True + + # Not prime + return False + + +def getprime(nbits): + """Returns a prime number of max. 'math.ceil(nbits/8)*8' bits. In + other words: nbits is rounded up to whole bytes. + + >>> p = getprime(8) + >>> is_prime(p-1) + 0 + >>> is_prime(p) + 1 + >>> is_prime(p+1) + 0 + """ + + while True: + integer = read_random_int(nbits) + + # Make sure it's odd + integer |= 1 + + # Test for primeness + if is_prime(integer): break + + # Retry if not prime + + return integer + +def are_relatively_prime(a, b): + """Returns True if a and b are relatively prime, and False if they + are not. + + >>> are_relatively_prime(2, 3) + 1 + >>> are_relatively_prime(2, 4) + 0 + """ + + d = gcd(a, b) + return (d == 1) + +def find_p_q(nbits): + """Returns a tuple of two different primes of nbits bits""" + pbits = nbits + (nbits/16) #Make sure that p and q aren't too close + qbits = nbits - (nbits/16) #or the factoring programs can factor n + p = getprime(pbits) + while True: + q = getprime(qbits) + #Make sure p and q are different. + if not q == p: break + return (p, q) + +def extended_gcd(a, b): + """Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb + """ + # r = gcd(a,b) i = multiplicitive inverse of a mod b + # or j = multiplicitive inverse of b mod a + # Neg return values for i or j are made positive mod b or a respectively + # Iterateive Version is faster and uses much less stack space + x = 0 + y = 1 + lx = 1 + ly = 0 + oa = a #Remember original a/b to remove + ob = b #negative values from return results + while b != 0: + q = long(a/b) + (a, b) = (b, a % b) + (x, lx) = ((lx - (q * x)),x) + (y, ly) = ((ly - (q * y)),y) + if (lx < 0): lx += ob #If neg wrap modulo orignal b + if (ly < 0): ly += oa #If neg wrap modulo orignal a + return (a, lx, ly) #Return only positive values + +# Main function: calculate encryption and decryption keys +def calculate_keys(p, q, nbits): + """Calculates an encryption and a decryption key for p and q, and + returns them as a tuple (e, d)""" + + n = p * q + phi_n = (p-1) * (q-1) + + while True: + # Make sure e has enough bits so we ensure "wrapping" through + # modulo n + e = max(65537,getprime(nbits/4)) + if are_relatively_prime(e, n) and are_relatively_prime(e, phi_n): break + + (d, i, j) = extended_gcd(e, phi_n) + + if not d == 1: + raise Exception("e (%d) and phi_n (%d) are not relatively prime" % (e, phi_n)) + if (i < 0): + raise Exception("New extended_gcd shouldn't return negative values") + if not (e * i) % phi_n == 1: + raise Exception("e (%d) and i (%d) are not mult. inv. modulo phi_n (%d)" % (e, i, phi_n)) + + return (e, i) + + +def gen_keys(nbits): + """Generate RSA keys of nbits bits. Returns (p, q, e, d). + + Note: this can take a long time, depending on the key size. + """ + + (p, q) = find_p_q(nbits) + (e, d) = calculate_keys(p, q, nbits) + + return (p, q, e, d) + +def newkeys(nbits): + """Generates public and private keys, and returns them as (pub, + priv). + + The public key consists of a dict {e: ..., , n: ....). The private + key consists of a dict {d: ...., p: ...., q: ....). + """ + nbits = max(9,nbits) # Don't let nbits go below 9 bits + (p, q, e, d) = gen_keys(nbits) + + return ( {'e': e, 'n': p*q}, {'d': d, 'p': p, 'q': q} ) + +def encrypt_int(message, ekey, n): + """Encrypts a message using encryption key 'ekey', working modulo n""" + + if type(message) is types.IntType: + message = long(message) + + if not type(message) is types.LongType: + raise TypeError("You must pass a long or int") + + if message < 0 or message > n: + raise OverflowError("The message is too long") + + #Note: Bit exponents start at zero (bit counts start at 1) this is correct + safebit = bit_size(n) - 2 #compute safe bit (MSB - 1) + message += (1 << safebit) #add safebit to ensure folding + + return pow(message, ekey, n) + +def decrypt_int(cyphertext, dkey, n): + """Decrypts a cypher text using the decryption key 'dkey', working + modulo n""" + + message = pow(cyphertext, dkey, n) + + safebit = bit_size(n) - 2 #compute safe bit (MSB - 1) + message -= (1 << safebit) #remove safebit before decode + + return message + +def encode64chops(chops): + """base64encodes chops and combines them into a ',' delimited string""" + + chips = [] #chips are character chops + + for value in chops: + chips.append(int2str64(value)) + + #delimit chops with comma + encoded = ','.join(chips) + + return encoded + +def decode64chops(string): + """base64decodes and makes a ',' delimited string into chops""" + + chips = string.split(',') #split chops at commas + + chops = [] + + for string in chips: #make char chops (chips) into chops + chops.append(str642int(string)) + + return chops + +def chopstring(message, key, n, funcref): + """Chops the 'message' into integers that fit into n, + leaving room for a safebit to be added to ensure that all + messages fold during exponentiation. The MSB of the number n + is not independant modulo n (setting it could cause overflow), so + use the next lower bit for the safebit. Therefore reserve 2-bits + in the number n for non-data bits. Calls specified encryption + function for each chop. + + Used by 'encrypt' and 'sign'. + """ + + msglen = len(message) + mbits = msglen * 8 + #Set aside 2-bits so setting of safebit won't overflow modulo n. + nbits = bit_size(n) - 2 # leave room for safebit + nbytes = nbits / 8 + blocks = msglen / nbytes + + if msglen % nbytes > 0: + blocks += 1 + + cypher = [] + + for bindex in range(blocks): + offset = bindex * nbytes + block = message[offset:offset+nbytes] + value = bytes2int(block) + cypher.append(funcref(value, key, n)) + + return encode64chops(cypher) #Encode encrypted ints to base64 strings + +def gluechops(string, key, n, funcref): + """Glues chops back together into a string. calls + funcref(integer, key, n) for each chop. + + Used by 'decrypt' and 'verify'. + """ + message = "" + + chops = decode64chops(string) #Decode base64 strings into integer chops + + for cpart in chops: + mpart = funcref(cpart, key, n) #Decrypt each chop + message += int2bytes(mpart) #Combine decrypted strings into a msg + + return message + +def encrypt(message, key): + """Encrypts a string 'message' with the public key 'key'""" + if 'n' not in key: + raise Exception("You must use the public key with encrypt") + + return chopstring(message, key['e'], key['n'], encrypt_int) + +def sign(message, key): + """Signs a string 'message' with the private key 'key'""" + if 'p' not in key: + raise Exception("You must use the private key with sign") + + return chopstring(message, key['d'], key['p']*key['q'], encrypt_int) + +def decrypt(cypher, key): + """Decrypts a string 'cypher' with the private key 'key'""" + if 'p' not in key: + raise Exception("You must use the private key with decrypt") + + return gluechops(cypher, key['d'], key['p']*key['q'], decrypt_int) + +def verify(cypher, key): + """Verifies a string 'cypher' with the public key 'key'""" + if 'n' not in key: + raise Exception("You must use the public key with verify") + + return gluechops(cypher, key['e'], key['n'], decrypt_int) + +# Do doctest if we're not imported +if __name__ == "__main__": + import doctest + doctest.testmod() + +__all__ = ["newkeys", "encrypt", "decrypt", "sign", "verify"] + diff --git a/src/rsa/asn1.py b/src/rsa/asn1.py new file mode 100644 index 00000000..6eb6da53 --- /dev/null +++ b/src/rsa/asn1.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''ASN.1 definitions. + +Not all ASN.1-handling code use these definitions, but when it does, they should be here. +''' + +from pyasn1.type import univ, namedtype, tag + +class PubKeyHeader(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('parameters', univ.Null()), + ) + +class OpenSSLPubKey(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('header', PubKeyHeader()), + + # This little hack (the implicit tag) allows us to get a Bit String as Octet String + namedtype.NamedType('key', univ.OctetString().subtype( + implicitTag=tag.Tag(tagClass=0, tagFormat=0, tagId=3))), + ) + + +class AsnPubKey(univ.Sequence): + '''ASN.1 contents of DER encoded public key: + + RSAPublicKey ::= SEQUENCE { + modulus INTEGER, -- n + publicExponent INTEGER, -- e + ''' + + componentType = namedtype.NamedTypes( + namedtype.NamedType('modulus', univ.Integer()), + namedtype.NamedType('publicExponent', univ.Integer()), + ) diff --git a/src/rsa/bigfile.py b/src/rsa/bigfile.py new file mode 100644 index 00000000..516cf56b --- /dev/null +++ b/src/rsa/bigfile.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Large file support + + - break a file into smaller blocks, and encrypt them, and store the + encrypted blocks in another file. + + - take such an encrypted files, decrypt its blocks, and reconstruct the + original file. + +The encrypted file format is as follows, where || denotes byte concatenation: + + FILE := VERSION || BLOCK || BLOCK ... + + BLOCK := LENGTH || DATA + + LENGTH := varint-encoded length of the subsequent data. Varint comes from + Google Protobuf, and encodes an integer into a variable number of bytes. + Each byte uses the 7 lowest bits to encode the value. The highest bit set + to 1 indicates the next byte is also part of the varint. The last byte will + have this bit set to 0. + +This file format is called the VARBLOCK format, in line with the varint format +used to denote the block sizes. + +''' + +from rsa import key, common, pkcs1, varblock +from rsa._compat import byte + +def encrypt_bigfile(infile, outfile, pub_key): + '''Encrypts a file, writing it to 'outfile' in VARBLOCK format. + + :param infile: file-like object to read the cleartext from + :param outfile: file-like object to write the crypto in VARBLOCK format to + :param pub_key: :py:class:`rsa.PublicKey` to encrypt with + + ''' + + if not isinstance(pub_key, key.PublicKey): + raise TypeError('Public key required, but got %r' % pub_key) + + key_bytes = common.bit_size(pub_key.n) // 8 + blocksize = key_bytes - 11 # keep space for PKCS#1 padding + + # Write the version number to the VARBLOCK file + outfile.write(byte(varblock.VARBLOCK_VERSION)) + + # Encrypt and write each block + for block in varblock.yield_fixedblocks(infile, blocksize): + crypto = pkcs1.encrypt(block, pub_key) + + varblock.write_varint(outfile, len(crypto)) + outfile.write(crypto) + +def decrypt_bigfile(infile, outfile, priv_key): + '''Decrypts an encrypted VARBLOCK file, writing it to 'outfile' + + :param infile: file-like object to read the crypto in VARBLOCK format from + :param outfile: file-like object to write the cleartext to + :param priv_key: :py:class:`rsa.PrivateKey` to decrypt with + + ''' + + if not isinstance(priv_key, key.PrivateKey): + raise TypeError('Private key required, but got %r' % priv_key) + + for block in varblock.yield_varblocks(infile): + cleartext = pkcs1.decrypt(block, priv_key) + outfile.write(cleartext) + +__all__ = ['encrypt_bigfile', 'decrypt_bigfile'] + diff --git a/src/rsa/cli.py b/src/rsa/cli.py new file mode 100644 index 00000000..527cc497 --- /dev/null +++ b/src/rsa/cli.py @@ -0,0 +1,379 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Commandline scripts. + +These scripts are called by the executables defined in setup.py. +''' + +from __future__ import with_statement, print_function + +import abc +import sys +from optparse import OptionParser + +import rsa +import rsa.bigfile +import rsa.pkcs1 + +HASH_METHODS = sorted(rsa.pkcs1.HASH_METHODS.keys()) + +def keygen(): + '''Key generator.''' + + # Parse the CLI options + parser = OptionParser(usage='usage: %prog [options] keysize', + description='Generates a new RSA keypair of "keysize" bits.') + + parser.add_option('--pubout', type='string', + help='Output filename for the public key. The public key is ' + 'not saved if this option is not present. You can use ' + 'pyrsa-priv2pub to create the public key file later.') + + parser.add_option('-o', '--out', type='string', + help='Output filename for the private key. The key is ' + 'written to stdout if this option is not present.') + + parser.add_option('--form', + help='key format of the private and public keys - default PEM', + choices=('PEM', 'DER'), default='PEM') + + (cli, cli_args) = parser.parse_args(sys.argv[1:]) + + if len(cli_args) != 1: + parser.print_help() + raise SystemExit(1) + + try: + keysize = int(cli_args[0]) + except ValueError: + parser.print_help() + print('Not a valid number: %s' % cli_args[0], file=sys.stderr) + raise SystemExit(1) + + print('Generating %i-bit key' % keysize, file=sys.stderr) + (pub_key, priv_key) = rsa.newkeys(keysize) + + + # Save public key + if cli.pubout: + print('Writing public key to %s' % cli.pubout, file=sys.stderr) + data = pub_key.save_pkcs1(format=cli.form) + with open(cli.pubout, 'wb') as outfile: + outfile.write(data) + + # Save private key + data = priv_key.save_pkcs1(format=cli.form) + + if cli.out: + print('Writing private key to %s' % cli.out, file=sys.stderr) + with open(cli.out, 'wb') as outfile: + outfile.write(data) + else: + print('Writing private key to stdout', file=sys.stderr) + sys.stdout.write(data) + + +class CryptoOperation(object): + '''CLI callable that operates with input, output, and a key.''' + + __metaclass__ = abc.ABCMeta + + keyname = 'public' # or 'private' + usage = 'usage: %%prog [options] %(keyname)s_key' + description = None + operation = 'decrypt' + operation_past = 'decrypted' + operation_progressive = 'decrypting' + input_help = 'Name of the file to %(operation)s. Reads from stdin if ' \ + 'not specified.' + output_help = 'Name of the file to write the %(operation_past)s file ' \ + 'to. Written to stdout if this option is not present.' + expected_cli_args = 1 + has_output = True + + key_class = rsa.PublicKey + + def __init__(self): + self.usage = self.usage % self.__class__.__dict__ + self.input_help = self.input_help % self.__class__.__dict__ + self.output_help = self.output_help % self.__class__.__dict__ + + @abc.abstractmethod + def perform_operation(self, indata, key, cli_args=None): + '''Performs the program's operation. + + Implement in a subclass. + + :returns: the data to write to the output. + ''' + + def __call__(self): + '''Runs the program.''' + + (cli, cli_args) = self.parse_cli() + + key = self.read_key(cli_args[0], cli.keyform) + + indata = self.read_infile(cli.input) + + print(self.operation_progressive.title(), file=sys.stderr) + outdata = self.perform_operation(indata, key, cli_args) + + if self.has_output: + self.write_outfile(outdata, cli.output) + + def parse_cli(self): + '''Parse the CLI options + + :returns: (cli_opts, cli_args) + ''' + + parser = OptionParser(usage=self.usage, description=self.description) + + parser.add_option('-i', '--input', type='string', help=self.input_help) + + if self.has_output: + parser.add_option('-o', '--output', type='string', help=self.output_help) + + parser.add_option('--keyform', + help='Key format of the %s key - default PEM' % self.keyname, + choices=('PEM', 'DER'), default='PEM') + + (cli, cli_args) = parser.parse_args(sys.argv[1:]) + + if len(cli_args) != self.expected_cli_args: + parser.print_help() + raise SystemExit(1) + + return (cli, cli_args) + + def read_key(self, filename, keyform): + '''Reads a public or private key.''' + + print('Reading %s key from %s' % (self.keyname, filename), file=sys.stderr) + with open(filename, 'rb') as keyfile: + keydata = keyfile.read() + + return self.key_class.load_pkcs1(keydata, keyform) + + def read_infile(self, inname): + '''Read the input file''' + + if inname: + print('Reading input from %s' % inname, file=sys.stderr) + with open(inname, 'rb') as infile: + return infile.read() + + print('Reading input from stdin', file=sys.stderr) + return sys.stdin.read() + + def write_outfile(self, outdata, outname): + '''Write the output file''' + + if outname: + print('Writing output to %s' % outname, file=sys.stderr) + with open(outname, 'wb') as outfile: + outfile.write(outdata) + else: + print('Writing output to stdout', file=sys.stderr) + sys.stdout.write(outdata) + +class EncryptOperation(CryptoOperation): + '''Encrypts a file.''' + + keyname = 'public' + description = ('Encrypts a file. The file must be shorter than the key ' + 'length in order to be encrypted. For larger files, use the ' + 'pyrsa-encrypt-bigfile command.') + operation = 'encrypt' + operation_past = 'encrypted' + operation_progressive = 'encrypting' + + + def perform_operation(self, indata, pub_key, cli_args=None): + '''Encrypts files.''' + + return rsa.encrypt(indata, pub_key) + +class DecryptOperation(CryptoOperation): + '''Decrypts a file.''' + + keyname = 'private' + description = ('Decrypts a file. The original file must be shorter than ' + 'the key length in order to have been encrypted. For larger ' + 'files, use the pyrsa-decrypt-bigfile command.') + operation = 'decrypt' + operation_past = 'decrypted' + operation_progressive = 'decrypting' + key_class = rsa.PrivateKey + + def perform_operation(self, indata, priv_key, cli_args=None): + '''Decrypts files.''' + + return rsa.decrypt(indata, priv_key) + +class SignOperation(CryptoOperation): + '''Signs a file.''' + + keyname = 'private' + usage = 'usage: %%prog [options] private_key hash_method' + description = ('Signs a file, outputs the signature. Choose the hash ' + 'method from %s' % ', '.join(HASH_METHODS)) + operation = 'sign' + operation_past = 'signature' + operation_progressive = 'Signing' + key_class = rsa.PrivateKey + expected_cli_args = 2 + + output_help = ('Name of the file to write the signature to. Written ' + 'to stdout if this option is not present.') + + def perform_operation(self, indata, priv_key, cli_args): + '''Decrypts files.''' + + hash_method = cli_args[1] + if hash_method not in HASH_METHODS: + raise SystemExit('Invalid hash method, choose one of %s' % + ', '.join(HASH_METHODS)) + + return rsa.sign(indata, priv_key, hash_method) + +class VerifyOperation(CryptoOperation): + '''Verify a signature.''' + + keyname = 'public' + usage = 'usage: %%prog [options] public_key signature_file' + description = ('Verifies a signature, exits with status 0 upon success, ' + 'prints an error message and exits with status 1 upon error.') + operation = 'verify' + operation_past = 'verified' + operation_progressive = 'Verifying' + key_class = rsa.PublicKey + expected_cli_args = 2 + has_output = False + + def perform_operation(self, indata, pub_key, cli_args): + '''Decrypts files.''' + + signature_file = cli_args[1] + + with open(signature_file, 'rb') as sigfile: + signature = sigfile.read() + + try: + rsa.verify(indata, signature, pub_key) + except rsa.VerificationError: + raise SystemExit('Verification failed.') + + print('Verification OK', file=sys.stderr) + + +class BigfileOperation(CryptoOperation): + '''CryptoOperation that doesn't read the entire file into memory.''' + + def __init__(self): + CryptoOperation.__init__(self) + + self.file_objects = [] + + def __del__(self): + '''Closes any open file handles.''' + + for fobj in self.file_objects: + fobj.close() + + def __call__(self): + '''Runs the program.''' + + (cli, cli_args) = self.parse_cli() + + key = self.read_key(cli_args[0], cli.keyform) + + # Get the file handles + infile = self.get_infile(cli.input) + outfile = self.get_outfile(cli.output) + + # Call the operation + print(self.operation_progressive.title(), file=sys.stderr) + self.perform_operation(infile, outfile, key, cli_args) + + def get_infile(self, inname): + '''Returns the input file object''' + + if inname: + print('Reading input from %s' % inname, file=sys.stderr) + fobj = open(inname, 'rb') + self.file_objects.append(fobj) + else: + print('Reading input from stdin', file=sys.stderr) + fobj = sys.stdin + + return fobj + + def get_outfile(self, outname): + '''Returns the output file object''' + + if outname: + print('Will write output to %s' % outname, file=sys.stderr) + fobj = open(outname, 'wb') + self.file_objects.append(fobj) + else: + print('Will write output to stdout', file=sys.stderr) + fobj = sys.stdout + + return fobj + +class EncryptBigfileOperation(BigfileOperation): + '''Encrypts a file to VARBLOCK format.''' + + keyname = 'public' + description = ('Encrypts a file to an encrypted VARBLOCK file. The file ' + 'can be larger than the key length, but the output file is only ' + 'compatible with Python-RSA.') + operation = 'encrypt' + operation_past = 'encrypted' + operation_progressive = 'encrypting' + + def perform_operation(self, infile, outfile, pub_key, cli_args=None): + '''Encrypts files to VARBLOCK.''' + + return rsa.bigfile.encrypt_bigfile(infile, outfile, pub_key) + +class DecryptBigfileOperation(BigfileOperation): + '''Decrypts a file in VARBLOCK format.''' + + keyname = 'private' + description = ('Decrypts an encrypted VARBLOCK file that was encrypted ' + 'with pyrsa-encrypt-bigfile') + operation = 'decrypt' + operation_past = 'decrypted' + operation_progressive = 'decrypting' + key_class = rsa.PrivateKey + + def perform_operation(self, infile, outfile, priv_key, cli_args=None): + '''Decrypts a VARBLOCK file.''' + + return rsa.bigfile.decrypt_bigfile(infile, outfile, priv_key) + + +encrypt = EncryptOperation() +decrypt = DecryptOperation() +sign = SignOperation() +verify = VerifyOperation() +encrypt_bigfile = EncryptBigfileOperation() +decrypt_bigfile = DecryptBigfileOperation() + diff --git a/src/rsa/common.py b/src/rsa/common.py new file mode 100644 index 00000000..39feb8c2 --- /dev/null +++ b/src/rsa/common.py @@ -0,0 +1,185 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Common functionality shared by several modules.''' + + +def bit_size(num): + ''' + Number of bits needed to represent a integer excluding any prefix + 0 bits. + + As per definition from http://wiki.python.org/moin/BitManipulation and + to match the behavior of the Python 3 API. + + Usage:: + + >>> bit_size(1023) + 10 + >>> bit_size(1024) + 11 + >>> bit_size(1025) + 11 + + :param num: + Integer value. If num is 0, returns 0. Only the absolute value of the + number is considered. Therefore, signed integers will be abs(num) + before the number's bit length is determined. + :returns: + Returns the number of bits in the integer. + ''' + if num == 0: + return 0 + if num < 0: + num = -num + + # Make sure this is an int and not a float. + num & 1 + + hex_num = "%x" % num + return ((len(hex_num) - 1) * 4) + { + '0':0, '1':1, '2':2, '3':2, + '4':3, '5':3, '6':3, '7':3, + '8':4, '9':4, 'a':4, 'b':4, + 'c':4, 'd':4, 'e':4, 'f':4, + }[hex_num[0]] + + +def _bit_size(number): + ''' + Returns the number of bits required to hold a specific long number. + ''' + if number < 0: + raise ValueError('Only nonnegative numbers possible: %s' % number) + + if number == 0: + return 0 + + # This works, even with very large numbers. When using math.log(number, 2), + # you'll get rounding errors and it'll fail. + bits = 0 + while number: + bits += 1 + number >>= 1 + + return bits + + +def byte_size(number): + ''' + Returns the number of bytes required to hold a specific long number. + + The number of bytes is rounded up. + + Usage:: + + >>> byte_size(1 << 1023) + 128 + >>> byte_size((1 << 1024) - 1) + 128 + >>> byte_size(1 << 1024) + 129 + + :param number: + An unsigned integer + :returns: + The number of bytes required to hold a specific long number. + ''' + quanta, mod = divmod(bit_size(number), 8) + if mod or number == 0: + quanta += 1 + return quanta + #return int(math.ceil(bit_size(number) / 8.0)) + + +def extended_gcd(a, b): + '''Returns a tuple (r, i, j) such that r = gcd(a, b) = ia + jb + ''' + # r = gcd(a,b) i = multiplicitive inverse of a mod b + # or j = multiplicitive inverse of b mod a + # Neg return values for i or j are made positive mod b or a respectively + # Iterateive Version is faster and uses much less stack space + x = 0 + y = 1 + lx = 1 + ly = 0 + oa = a #Remember original a/b to remove + ob = b #negative values from return results + while b != 0: + q = a // b + (a, b) = (b, a % b) + (x, lx) = ((lx - (q * x)),x) + (y, ly) = ((ly - (q * y)),y) + if (lx < 0): lx += ob #If neg wrap modulo orignal b + if (ly < 0): ly += oa #If neg wrap modulo orignal a + return (a, lx, ly) #Return only positive values + + +def inverse(x, n): + '''Returns x^-1 (mod n) + + >>> inverse(7, 4) + 3 + >>> (inverse(143, 4) * 143) % 4 + 1 + ''' + + (divider, inv, _) = extended_gcd(x, n) + + if divider != 1: + raise ValueError("x (%d) and n (%d) are not relatively prime" % (x, n)) + + return inv + + +def crt(a_values, modulo_values): + '''Chinese Remainder Theorem. + + Calculates x such that x = a[i] (mod m[i]) for each i. + + :param a_values: the a-values of the above equation + :param modulo_values: the m-values of the above equation + :returns: x such that x = a[i] (mod m[i]) for each i + + + >>> crt([2, 3], [3, 5]) + 8 + + >>> crt([2, 3, 2], [3, 5, 7]) + 23 + + >>> crt([2, 3, 0], [7, 11, 15]) + 135 + ''' + + m = 1 + x = 0 + + for modulo in modulo_values: + m *= modulo + + for (m_i, a_i) in zip(modulo_values, a_values): + M_i = m // m_i + inv = inverse(M_i, m_i) + + x = (x + a_i * M_i * inv) % m + + return x + +if __name__ == '__main__': + import doctest + doctest.testmod() + diff --git a/src/rsa/core.py b/src/rsa/core.py new file mode 100644 index 00000000..90dfee8e --- /dev/null +++ b/src/rsa/core.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Core mathematical operations. + +This is the actual core RSA implementation, which is only defined +mathematically on integers. +''' + + +from rsa._compat import is_integer + +def assert_int(var, name): + + if is_integer(var): + return + + raise TypeError('%s should be an integer, not %s' % (name, var.__class__)) + +def encrypt_int(message, ekey, n): + '''Encrypts a message using encryption key 'ekey', working modulo n''' + + assert_int(message, 'message') + assert_int(ekey, 'ekey') + assert_int(n, 'n') + + if message < 0: + raise ValueError('Only non-negative numbers are supported') + + if message > n: + raise OverflowError("The message %i is too long for n=%i" % (message, n)) + + return pow(message, ekey, n) + +def decrypt_int(cyphertext, dkey, n): + '''Decrypts a cypher text using the decryption key 'dkey', working + modulo n''' + + assert_int(cyphertext, 'cyphertext') + assert_int(dkey, 'dkey') + assert_int(n, 'n') + + message = pow(cyphertext, dkey, n) + return message + diff --git a/src/rsa/key.py b/src/rsa/key.py new file mode 100644 index 00000000..b6de7b3f --- /dev/null +++ b/src/rsa/key.py @@ -0,0 +1,612 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''RSA key generation code. + +Create new keys with the newkeys() function. It will give you a PublicKey and a +PrivateKey object. + +Loading and saving keys requires the pyasn1 module. This module is imported as +late as possible, such that other functionality will remain working in absence +of pyasn1. + +''' + +import logging +from rsa._compat import b, bytes_type + +import rsa.prime +import rsa.pem +import rsa.common + +log = logging.getLogger(__name__) + + + +class AbstractKey(object): + '''Abstract superclass for private and public keys.''' + + @classmethod + def load_pkcs1(cls, keyfile, format='PEM'): + r'''Loads a key in PKCS#1 DER or PEM format. + + :param keyfile: contents of a DER- or PEM-encoded file that contains + the public key. + :param format: the format of the file to load; 'PEM' or 'DER' + + :return: a PublicKey object + + ''' + + methods = { + 'PEM': cls._load_pkcs1_pem, + 'DER': cls._load_pkcs1_der, + } + + if format not in methods: + formats = ', '.join(sorted(methods.keys())) + raise ValueError('Unsupported format: %r, try one of %s' % (format, + formats)) + + method = methods[format] + return method(keyfile) + + def save_pkcs1(self, format='PEM'): + '''Saves the public key in PKCS#1 DER or PEM format. + + :param format: the format to save; 'PEM' or 'DER' + :returns: the DER- or PEM-encoded public key. + + ''' + + methods = { + 'PEM': self._save_pkcs1_pem, + 'DER': self._save_pkcs1_der, + } + + if format not in methods: + formats = ', '.join(sorted(methods.keys())) + raise ValueError('Unsupported format: %r, try one of %s' % (format, + formats)) + + method = methods[format] + return method() + +class PublicKey(AbstractKey): + '''Represents a public RSA key. + + This key is also known as the 'encryption key'. It contains the 'n' and 'e' + values. + + Supports attributes as well as dictionary-like access. Attribute accesss is + faster, though. + + >>> PublicKey(5, 3) + PublicKey(5, 3) + + >>> key = PublicKey(5, 3) + >>> key.n + 5 + >>> key['n'] + 5 + >>> key.e + 3 + >>> key['e'] + 3 + + ''' + + __slots__ = ('n', 'e') + + def __init__(self, n, e): + self.n = n + self.e = e + + def __getitem__(self, key): + return getattr(self, key) + + def __repr__(self): + return 'PublicKey(%i, %i)' % (self.n, self.e) + + def __eq__(self, other): + if other is None: + return False + + if not isinstance(other, PublicKey): + return False + + return self.n == other.n and self.e == other.e + + def __ne__(self, other): + return not (self == other) + + @classmethod + def _load_pkcs1_der(cls, keyfile): + r'''Loads a key in PKCS#1 DER format. + + @param keyfile: contents of a DER-encoded file that contains the public + key. + @return: a PublicKey object + + First let's construct a DER encoded key: + + >>> import base64 + >>> b64der = 'MAwCBQCNGmYtAgMBAAE=' + >>> der = base64.decodestring(b64der) + + This loads the file: + + >>> PublicKey._load_pkcs1_der(der) + PublicKey(2367317549, 65537) + + ''' + + from pyasn1.codec.der import decoder + from rsa.asn1 import AsnPubKey + + (priv, _) = decoder.decode(keyfile, asn1Spec=AsnPubKey()) + return cls(n=int(priv['modulus']), e=int(priv['publicExponent'])) + + def _save_pkcs1_der(self): + '''Saves the public key in PKCS#1 DER format. + + @returns: the DER-encoded public key. + ''' + + from pyasn1.codec.der import encoder + from rsa.asn1 import AsnPubKey + + # Create the ASN object + asn_key = AsnPubKey() + asn_key.setComponentByName('modulus', self.n) + asn_key.setComponentByName('publicExponent', self.e) + + return encoder.encode(asn_key) + + @classmethod + def _load_pkcs1_pem(cls, keyfile): + '''Loads a PKCS#1 PEM-encoded public key file. + + The contents of the file before the "-----BEGIN RSA PUBLIC KEY-----" and + after the "-----END RSA PUBLIC KEY-----" lines is ignored. + + @param keyfile: contents of a PEM-encoded file that contains the public + key. + @return: a PublicKey object + ''' + + der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY') + return cls._load_pkcs1_der(der) + + def _save_pkcs1_pem(self): + '''Saves a PKCS#1 PEM-encoded public key file. + + @return: contents of a PEM-encoded file that contains the public key. + ''' + + der = self._save_pkcs1_der() + return rsa.pem.save_pem(der, 'RSA PUBLIC KEY') + + @classmethod + def load_pkcs1_openssl_pem(cls, keyfile): + '''Loads a PKCS#1.5 PEM-encoded public key file from OpenSSL. + + These files can be recognised in that they start with BEGIN PUBLIC KEY + rather than BEGIN RSA PUBLIC KEY. + + The contents of the file before the "-----BEGIN PUBLIC KEY-----" and + after the "-----END PUBLIC KEY-----" lines is ignored. + + @param keyfile: contents of a PEM-encoded file that contains the public + key, from OpenSSL. + @return: a PublicKey object + ''' + + der = rsa.pem.load_pem(keyfile, 'PUBLIC KEY') + return cls.load_pkcs1_openssl_der(der) + + @classmethod + def load_pkcs1_openssl_der(cls, keyfile): + '''Loads a PKCS#1 DER-encoded public key file from OpenSSL. + + @param keyfile: contents of a DER-encoded file that contains the public + key, from OpenSSL. + @return: a PublicKey object + ''' + + from rsa.asn1 import OpenSSLPubKey + from pyasn1.codec.der import decoder + from pyasn1.type import univ + + (keyinfo, _) = decoder.decode(keyfile, asn1Spec=OpenSSLPubKey()) + + if keyinfo['header']['oid'] != univ.ObjectIdentifier('1.2.840.113549.1.1.1'): + raise TypeError("This is not a DER-encoded OpenSSL-compatible public key") + + return cls._load_pkcs1_der(keyinfo['key'][1:]) + + + + +class PrivateKey(AbstractKey): + '''Represents a private RSA key. + + This key is also known as the 'decryption key'. It contains the 'n', 'e', + 'd', 'p', 'q' and other values. + + Supports attributes as well as dictionary-like access. Attribute accesss is + faster, though. + + >>> PrivateKey(3247, 65537, 833, 191, 17) + PrivateKey(3247, 65537, 833, 191, 17) + + exp1, exp2 and coef don't have to be given, they will be calculated: + + >>> pk = PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + >>> pk.exp1 + 55063 + >>> pk.exp2 + 10095 + >>> pk.coef + 50797 + + If you give exp1, exp2 or coef, they will be used as-is: + + >>> pk = PrivateKey(1, 2, 3, 4, 5, 6, 7, 8) + >>> pk.exp1 + 6 + >>> pk.exp2 + 7 + >>> pk.coef + 8 + + ''' + + __slots__ = ('n', 'e', 'd', 'p', 'q', 'exp1', 'exp2', 'coef') + + def __init__(self, n, e, d, p, q, exp1=None, exp2=None, coef=None): + self.n = n + self.e = e + self.d = d + self.p = p + self.q = q + + # Calculate the other values if they aren't supplied + if exp1 is None: + self.exp1 = int(d % (p - 1)) + else: + self.exp1 = exp1 + + if exp1 is None: + self.exp2 = int(d % (q - 1)) + else: + self.exp2 = exp2 + + if coef is None: + self.coef = rsa.common.inverse(q, p) + else: + self.coef = coef + + def __getitem__(self, key): + return getattr(self, key) + + def __repr__(self): + return 'PrivateKey(%(n)i, %(e)i, %(d)i, %(p)i, %(q)i)' % self + + def __eq__(self, other): + if other is None: + return False + + if not isinstance(other, PrivateKey): + return False + + return (self.n == other.n and + self.e == other.e and + self.d == other.d and + self.p == other.p and + self.q == other.q and + self.exp1 == other.exp1 and + self.exp2 == other.exp2 and + self.coef == other.coef) + + def __ne__(self, other): + return not (self == other) + + @classmethod + def _load_pkcs1_der(cls, keyfile): + r'''Loads a key in PKCS#1 DER format. + + @param keyfile: contents of a DER-encoded file that contains the private + key. + @return: a PrivateKey object + + First let's construct a DER encoded key: + + >>> import base64 + >>> b64der = 'MC4CAQACBQDeKYlRAgMBAAECBQDHn4npAgMA/icCAwDfxwIDANcXAgInbwIDAMZt' + >>> der = base64.decodestring(b64der) + + This loads the file: + + >>> PrivateKey._load_pkcs1_der(der) + PrivateKey(3727264081, 65537, 3349121513, 65063, 57287) + + ''' + + from pyasn1.codec.der import decoder + (priv, _) = decoder.decode(keyfile) + + # ASN.1 contents of DER encoded private key: + # + # RSAPrivateKey ::= SEQUENCE { + # version Version, + # modulus INTEGER, -- n + # publicExponent INTEGER, -- e + # privateExponent INTEGER, -- d + # prime1 INTEGER, -- p + # prime2 INTEGER, -- q + # exponent1 INTEGER, -- d mod (p-1) + # exponent2 INTEGER, -- d mod (q-1) + # coefficient INTEGER, -- (inverse of q) mod p + # otherPrimeInfos OtherPrimeInfos OPTIONAL + # } + + if priv[0] != 0: + raise ValueError('Unable to read this file, version %s != 0' % priv[0]) + + as_ints = tuple(int(x) for x in priv[1:9]) + return cls(*as_ints) + + def _save_pkcs1_der(self): + '''Saves the private key in PKCS#1 DER format. + + @returns: the DER-encoded private key. + ''' + + from pyasn1.type import univ, namedtype + from pyasn1.codec.der import encoder + + class AsnPrivKey(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', univ.Integer()), + namedtype.NamedType('modulus', univ.Integer()), + namedtype.NamedType('publicExponent', univ.Integer()), + namedtype.NamedType('privateExponent', univ.Integer()), + namedtype.NamedType('prime1', univ.Integer()), + namedtype.NamedType('prime2', univ.Integer()), + namedtype.NamedType('exponent1', univ.Integer()), + namedtype.NamedType('exponent2', univ.Integer()), + namedtype.NamedType('coefficient', univ.Integer()), + ) + + # Create the ASN object + asn_key = AsnPrivKey() + asn_key.setComponentByName('version', 0) + asn_key.setComponentByName('modulus', self.n) + asn_key.setComponentByName('publicExponent', self.e) + asn_key.setComponentByName('privateExponent', self.d) + asn_key.setComponentByName('prime1', self.p) + asn_key.setComponentByName('prime2', self.q) + asn_key.setComponentByName('exponent1', self.exp1) + asn_key.setComponentByName('exponent2', self.exp2) + asn_key.setComponentByName('coefficient', self.coef) + + return encoder.encode(asn_key) + + @classmethod + def _load_pkcs1_pem(cls, keyfile): + '''Loads a PKCS#1 PEM-encoded private key file. + + The contents of the file before the "-----BEGIN RSA PRIVATE KEY-----" and + after the "-----END RSA PRIVATE KEY-----" lines is ignored. + + @param keyfile: contents of a PEM-encoded file that contains the private + key. + @return: a PrivateKey object + ''' + + der = rsa.pem.load_pem(keyfile, b('RSA PRIVATE KEY')) + return cls._load_pkcs1_der(der) + + def _save_pkcs1_pem(self): + '''Saves a PKCS#1 PEM-encoded private key file. + + @return: contents of a PEM-encoded file that contains the private key. + ''' + + der = self._save_pkcs1_der() + return rsa.pem.save_pem(der, b('RSA PRIVATE KEY')) + +def find_p_q(nbits, getprime_func=rsa.prime.getprime, accurate=True): + ''''Returns a tuple of two different primes of nbits bits each. + + The resulting p * q has exacty 2 * nbits bits, and the returned p and q + will not be equal. + + :param nbits: the number of bits in each of p and q. + :param getprime_func: the getprime function, defaults to + :py:func:`rsa.prime.getprime`. + + *Introduced in Python-RSA 3.1* + + :param accurate: whether to enable accurate mode or not. + :returns: (p, q), where p > q + + >>> (p, q) = find_p_q(128) + >>> from rsa import common + >>> common.bit_size(p * q) + 256 + + When not in accurate mode, the number of bits can be slightly less + + >>> (p, q) = find_p_q(128, accurate=False) + >>> from rsa import common + >>> common.bit_size(p * q) <= 256 + True + >>> common.bit_size(p * q) > 240 + True + + ''' + + total_bits = nbits * 2 + + # Make sure that p and q aren't too close or the factoring programs can + # factor n. + shift = nbits // 16 + pbits = nbits + shift + qbits = nbits - shift + + # Choose the two initial primes + log.debug('find_p_q(%i): Finding p', nbits) + p = getprime_func(pbits) + log.debug('find_p_q(%i): Finding q', nbits) + q = getprime_func(qbits) + + def is_acceptable(p, q): + '''Returns True iff p and q are acceptable: + + - p and q differ + - (p * q) has the right nr of bits (when accurate=True) + ''' + + if p == q: + return False + + if not accurate: + return True + + # Make sure we have just the right amount of bits + found_size = rsa.common.bit_size(p * q) + return total_bits == found_size + + # Keep choosing other primes until they match our requirements. + change_p = False + while not is_acceptable(p, q): + # Change p on one iteration and q on the other + if change_p: + p = getprime_func(pbits) + else: + q = getprime_func(qbits) + + change_p = not change_p + + # We want p > q as described on + # http://www.di-mgt.com.au/rsa_alg.html#crt + return (max(p, q), min(p, q)) + +def calculate_keys(p, q, nbits): + '''Calculates an encryption and a decryption key given p and q, and + returns them as a tuple (e, d) + + ''' + + phi_n = (p - 1) * (q - 1) + + # A very common choice for e is 65537 + e = 65537 + + try: + d = rsa.common.inverse(e, phi_n) + except ValueError: + raise ValueError("e (%d) and phi_n (%d) are not relatively prime" % + (e, phi_n)) + + if (e * d) % phi_n != 1: + raise ValueError("e (%d) and d (%d) are not mult. inv. modulo " + "phi_n (%d)" % (e, d, phi_n)) + + return (e, d) + +def gen_keys(nbits, getprime_func, accurate=True): + '''Generate RSA keys of nbits bits. Returns (p, q, e, d). + + Note: this can take a long time, depending on the key size. + + :param nbits: the total number of bits in ``p`` and ``q``. Both ``p`` and + ``q`` will use ``nbits/2`` bits. + :param getprime_func: either :py:func:`rsa.prime.getprime` or a function + with similar signature. + ''' + + (p, q) = find_p_q(nbits // 2, getprime_func, accurate) + (e, d) = calculate_keys(p, q, nbits // 2) + + return (p, q, e, d) + +def newkeys(nbits, accurate=True, poolsize=1): + '''Generates public and private keys, and returns them as (pub, priv). + + The public key is also known as the 'encryption key', and is a + :py:class:`rsa.PublicKey` object. The private key is also known as the + 'decryption key' and is a :py:class:`rsa.PrivateKey` object. + + :param nbits: the number of bits required to store ``n = p*q``. + :param accurate: when True, ``n`` will have exactly the number of bits you + asked for. However, this makes key generation much slower. When False, + `n`` may have slightly less bits. + :param poolsize: the number of processes to use to generate the prime + numbers. If set to a number > 1, a parallel algorithm will be used. + This requires Python 2.6 or newer. + + :returns: a tuple (:py:class:`rsa.PublicKey`, :py:class:`rsa.PrivateKey`) + + The ``poolsize`` parameter was added in *Python-RSA 3.1* and requires + Python 2.6 or newer. + + ''' + + if nbits < 16: + raise ValueError('Key too small') + + if poolsize < 1: + raise ValueError('Pool size (%i) should be >= 1' % poolsize) + + # Determine which getprime function to use + if poolsize > 1: + from rsa import parallel + import functools + + getprime_func = functools.partial(parallel.getprime, poolsize=poolsize) + else: getprime_func = rsa.prime.getprime + + # Generate the key components + (p, q, e, d) = gen_keys(nbits, getprime_func) + + # Create the key objects + n = p * q + + return ( + PublicKey(n, e), + PrivateKey(n, e, d, p, q) + ) + +__all__ = ['PublicKey', 'PrivateKey', 'newkeys'] + +if __name__ == '__main__': + import doctest + + try: + for count in range(100): + (failures, tests) = doctest.testmod() + if failures: + break + + if (count and count % 10 == 0) or count == 1: + print('%i times' % count) + except KeyboardInterrupt: + print('Aborted') + else: + print('Doctests done') diff --git a/src/rsa/parallel.py b/src/rsa/parallel.py new file mode 100644 index 00000000..e5034ac7 --- /dev/null +++ b/src/rsa/parallel.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Functions for parallel computation on multiple cores. + +Introduced in Python-RSA 3.1. + +.. note:: + + Requires Python 2.6 or newer. + +''' + +from __future__ import print_function + +import multiprocessing as mp + +import rsa.prime +import rsa.randnum + +def _find_prime(nbits, pipe): + while True: + integer = rsa.randnum.read_random_int(nbits) + + # Make sure it's odd + integer |= 1 + + # Test for primeness + if rsa.prime.is_prime(integer): + pipe.send(integer) + return + +def getprime(nbits, poolsize): + '''Returns a prime number that can be stored in 'nbits' bits. + + Works in multiple threads at the same time. + + >>> p = getprime(128, 3) + >>> rsa.prime.is_prime(p-1) + False + >>> rsa.prime.is_prime(p) + True + >>> rsa.prime.is_prime(p+1) + False + + >>> from rsa import common + >>> common.bit_size(p) == 128 + True + + ''' + + (pipe_recv, pipe_send) = mp.Pipe(duplex=False) + + # Create processes + procs = [mp.Process(target=_find_prime, args=(nbits, pipe_send)) + for _ in range(poolsize)] + [p.start() for p in procs] + + result = pipe_recv.recv() + + [p.terminate() for p in procs] + + return result + +__all__ = ['getprime'] + + +if __name__ == '__main__': + print('Running doctests 1000x or until failure') + import doctest + + for count in range(100): + (failures, tests) = doctest.testmod() + if failures: + break + + if count and count % 10 == 0: + print('%i times' % count) + + print('Doctests done') + diff --git a/src/rsa/pem.py b/src/rsa/pem.py new file mode 100644 index 00000000..b1c3a0ed --- /dev/null +++ b/src/rsa/pem.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Functions that load and write PEM-encoded files.''' + +import base64 +from rsa._compat import b, is_bytes + +def _markers(pem_marker): + ''' + Returns the start and end PEM markers + ''' + + if is_bytes(pem_marker): + pem_marker = pem_marker.decode('utf-8') + + return (b('-----BEGIN %s-----' % pem_marker), + b('-----END %s-----' % pem_marker)) + +def load_pem(contents, pem_marker): + '''Loads a PEM file. + + @param contents: the contents of the file to interpret + @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' + when your file has '-----BEGIN RSA PRIVATE KEY-----' and + '-----END RSA PRIVATE KEY-----' markers. + + @return the base64-decoded content between the start and end markers. + + @raise ValueError: when the content is invalid, for example when the start + marker cannot be found. + + ''' + + (pem_start, pem_end) = _markers(pem_marker) + + pem_lines = [] + in_pem_part = False + + for line in contents.splitlines(): + line = line.strip() + + # Skip empty lines + if not line: + continue + + # Handle start marker + if line == pem_start: + if in_pem_part: + raise ValueError('Seen start marker "%s" twice' % pem_start) + + in_pem_part = True + continue + + # Skip stuff before first marker + if not in_pem_part: + continue + + # Handle end marker + if in_pem_part and line == pem_end: + in_pem_part = False + break + + # Load fields + if b(':') in line: + continue + + pem_lines.append(line) + + # Do some sanity checks + if not pem_lines: + raise ValueError('No PEM start marker "%s" found' % pem_start) + + if in_pem_part: + raise ValueError('No PEM end marker "%s" found' % pem_end) + + # Base64-decode the contents + pem = b('').join(pem_lines) + return base64.decodestring(pem) + + +def save_pem(contents, pem_marker): + '''Saves a PEM file. + + @param contents: the contents to encode in PEM format + @param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' + when your file has '-----BEGIN RSA PRIVATE KEY-----' and + '-----END RSA PRIVATE KEY-----' markers. + + @return the base64-encoded content between the start and end markers. + + ''' + + (pem_start, pem_end) = _markers(pem_marker) + + b64 = base64.encodestring(contents).replace(b('\n'), b('')) + pem_lines = [pem_start] + + for block_start in range(0, len(b64), 64): + block = b64[block_start:block_start + 64] + pem_lines.append(block) + + pem_lines.append(pem_end) + pem_lines.append(b('')) + + return b('\n').join(pem_lines) + diff --git a/src/rsa/pkcs1.py b/src/rsa/pkcs1.py new file mode 100644 index 00000000..0e51928a --- /dev/null +++ b/src/rsa/pkcs1.py @@ -0,0 +1,373 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Functions for PKCS#1 version 1.5 encryption and signing + +This module implements certain functionality from PKCS#1 version 1.5. For a +very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes + +At least 8 bytes of random padding is used when encrypting a message. This makes +these methods much more secure than the ones in the ``rsa`` module. + +WARNING: this module leaks information when decryption fails. The exceptions +that are raised contain the Python traceback information, which can be used to +deduce where in the process the failure occurred. DO NOT PASS SUCH INFORMATION +to your users. +''' + +import hashlib +import os + +from rsa._compat import b +from rsa import common, transform, core, varblock + +# ASN.1 codes that describe the hash algorithm used. +HASH_ASN1 = { + 'MD5': b('\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'), + 'SHA-1': b('\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'), + 'SHA-256': b('\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'), + 'SHA-384': b('\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'), + 'SHA-512': b('\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'), +} + +HASH_METHODS = { + 'MD5': hashlib.md5, + 'SHA-1': hashlib.sha1, + 'SHA-256': hashlib.sha256, + 'SHA-384': hashlib.sha384, + 'SHA-512': hashlib.sha512, +} + +class CryptoError(Exception): + '''Base class for all exceptions in this module.''' + +class DecryptionError(CryptoError): + '''Raised when decryption fails.''' + +class VerificationError(CryptoError): + '''Raised when verification fails.''' + +def _pad_for_encryption(message, target_length): + r'''Pads the message for encryption, returning the padded message. + + :return: 00 02 RANDOM_DATA 00 MESSAGE + + >>> block = _pad_for_encryption('hello', 16) + >>> len(block) + 16 + >>> block[0:2] + '\x00\x02' + >>> block[-6:] + '\x00hello' + + ''' + + max_msglength = target_length - 11 + msglength = len(message) + + if msglength > max_msglength: + raise OverflowError('%i bytes needed for message, but there is only' + ' space for %i' % (msglength, max_msglength)) + + # Get random padding + padding = b('') + padding_length = target_length - msglength - 3 + + # We remove 0-bytes, so we'll end up with less padding than we've asked for, + # so keep adding data until we're at the correct length. + while len(padding) < padding_length: + needed_bytes = padding_length - len(padding) + + # Always read at least 8 bytes more than we need, and trim off the rest + # after removing the 0-bytes. This increases the chance of getting + # enough bytes, especially when needed_bytes is small + new_padding = os.urandom(needed_bytes + 5) + new_padding = new_padding.replace(b('\x00'), b('')) + padding = padding + new_padding[:needed_bytes] + + assert len(padding) == padding_length + + return b('').join([b('\x00\x02'), + padding, + b('\x00'), + message]) + + +def _pad_for_signing(message, target_length): + r'''Pads the message for signing, returning the padded message. + + The padding is always a repetition of FF bytes. + + :return: 00 01 PADDING 00 MESSAGE + + >>> block = _pad_for_signing('hello', 16) + >>> len(block) + 16 + >>> block[0:2] + '\x00\x01' + >>> block[-6:] + '\x00hello' + >>> block[2:-6] + '\xff\xff\xff\xff\xff\xff\xff\xff' + + ''' + + max_msglength = target_length - 11 + msglength = len(message) + + if msglength > max_msglength: + raise OverflowError('%i bytes needed for message, but there is only' + ' space for %i' % (msglength, max_msglength)) + + padding_length = target_length - msglength - 3 + + return b('').join([b('\x00\x01'), + padding_length * b('\xff'), + b('\x00'), + message]) + + +def encrypt(message, pub_key): + '''Encrypts the given message using PKCS#1 v1.5 + + :param message: the message to encrypt. Must be a byte string no longer than + ``k-11`` bytes, where ``k`` is the number of bytes needed to encode + the ``n`` component of the public key. + :param pub_key: the :py:class:`rsa.PublicKey` to encrypt with. + :raise OverflowError: when the message is too large to fit in the padded + block. + + >>> from rsa import key, common + >>> (pub_key, priv_key) = key.newkeys(256) + >>> message = 'hello' + >>> crypto = encrypt(message, pub_key) + + The crypto text should be just as long as the public key 'n' component: + + >>> len(crypto) == common.byte_size(pub_key.n) + True + + ''' + + keylength = common.byte_size(pub_key.n) + padded = _pad_for_encryption(message, keylength) + + payload = transform.bytes2int(padded) + encrypted = core.encrypt_int(payload, pub_key.e, pub_key.n) + block = transform.int2bytes(encrypted, keylength) + + return block + +def decrypt(crypto, priv_key): + r'''Decrypts the given message using PKCS#1 v1.5 + + The decryption is considered 'failed' when the resulting cleartext doesn't + start with the bytes 00 02, or when the 00 byte between the padding and + the message cannot be found. + + :param crypto: the crypto text as returned by :py:func:`rsa.encrypt` + :param priv_key: the :py:class:`rsa.PrivateKey` to decrypt with. + :raise DecryptionError: when the decryption fails. No details are given as + to why the code thinks the decryption fails, as this would leak + information about the private key. + + + >>> import rsa + >>> (pub_key, priv_key) = rsa.newkeys(256) + + It works with strings: + + >>> crypto = encrypt('hello', pub_key) + >>> decrypt(crypto, priv_key) + 'hello' + + And with binary data: + + >>> crypto = encrypt('\x00\x00\x00\x00\x01', pub_key) + >>> decrypt(crypto, priv_key) + '\x00\x00\x00\x00\x01' + + Altering the encrypted information will *likely* cause a + :py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use + :py:func:`rsa.sign`. + + + .. warning:: + + Never display the stack trace of a + :py:class:`rsa.pkcs1.DecryptionError` exception. It shows where in the + code the exception occurred, and thus leaks information about the key. + It's only a tiny bit of information, but every bit makes cracking the + keys easier. + + >>> crypto = encrypt('hello', pub_key) + >>> crypto = crypto[0:5] + 'X' + crypto[6:] # change a byte + >>> decrypt(crypto, priv_key) + Traceback (most recent call last): + ... + DecryptionError: Decryption failed + + ''' + + blocksize = common.byte_size(priv_key.n) + encrypted = transform.bytes2int(crypto) + decrypted = core.decrypt_int(encrypted, priv_key.d, priv_key.n) + cleartext = transform.int2bytes(decrypted, blocksize) + + # If we can't find the cleartext marker, decryption failed. + if cleartext[0:2] != b('\x00\x02'): + raise DecryptionError('Decryption failed') + + # Find the 00 separator between the padding and the message + try: + sep_idx = cleartext.index(b('\x00'), 2) + except ValueError: + raise DecryptionError('Decryption failed') + + return cleartext[sep_idx+1:] + +def sign(message, priv_key, hash): + '''Signs the message with the private key. + + Hashes the message, then signs the hash with the given key. This is known + as a "detached signature", because the message itself isn't altered. + + :param message: the message to sign. Can be an 8-bit string or a file-like + object. If ``message`` has a ``read()`` method, it is assumed to be a + file-like object. + :param priv_key: the :py:class:`rsa.PrivateKey` to sign with + :param hash: the hash method used on the message. Use 'MD5', 'SHA-1', + 'SHA-256', 'SHA-384' or 'SHA-512'. + :return: a message signature block. + :raise OverflowError: if the private key is too small to contain the + requested hash. + + ''' + + # Get the ASN1 code for this hash method + if hash not in HASH_ASN1: + raise ValueError('Invalid hash method: %s' % hash) + asn1code = HASH_ASN1[hash] + + # Calculate the hash + hash = _hash(message, hash) + + # Encrypt the hash with the private key + cleartext = asn1code + hash + keylength = common.byte_size(priv_key.n) + padded = _pad_for_signing(cleartext, keylength) + + payload = transform.bytes2int(padded) + encrypted = core.encrypt_int(payload, priv_key.d, priv_key.n) + block = transform.int2bytes(encrypted, keylength) + + return block + +def verify(message, signature, pub_key): + '''Verifies that the signature matches the message. + + The hash method is detected automatically from the signature. + + :param message: the signed message. Can be an 8-bit string or a file-like + object. If ``message`` has a ``read()`` method, it is assumed to be a + file-like object. + :param signature: the signature block, as created with :py:func:`rsa.sign`. + :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message. + :raise VerificationError: when the signature doesn't match the message. + + ''' + + keylength = common.byte_size(pub_key.n) + encrypted = transform.bytes2int(signature) + decrypted = core.decrypt_int(encrypted, pub_key.e, pub_key.n) + clearsig = transform.int2bytes(decrypted, keylength) + + # Get the hash method + method_name = _find_method_hash(clearsig) + message_hash = _hash(message, method_name) + + # Reconstruct the expected padded hash + cleartext = HASH_ASN1[method_name] + message_hash + expected = _pad_for_signing(cleartext, keylength) + + # Compare with the signed one + if expected != clearsig: + raise VerificationError('Verification failed') + + return True + +def _hash(message, method_name): + '''Returns the message digest. + + :param message: the signed message. Can be an 8-bit string or a file-like + object. If ``message`` has a ``read()`` method, it is assumed to be a + file-like object. + :param method_name: the hash method, must be a key of + :py:const:`HASH_METHODS`. + + ''' + + if method_name not in HASH_METHODS: + raise ValueError('Invalid hash method: %s' % method_name) + + method = HASH_METHODS[method_name] + hasher = method() + + if hasattr(message, 'read') and hasattr(message.read, '__call__'): + # read as 1K blocks + for block in varblock.yield_fixedblocks(message, 1024): + hasher.update(block) + else: + # hash the message object itself. + hasher.update(message) + + return hasher.digest() + + +def _find_method_hash(clearsig): + '''Finds the hash method. + + :param clearsig: full padded ASN1 and hash. + + :return: the used hash method. + + :raise VerificationFailed: when the hash method cannot be found + + ''' + + for (hashname, asn1code) in HASH_ASN1.items(): + if asn1code in clearsig: + return hashname + + raise VerificationError('Verification failed') + + +__all__ = ['encrypt', 'decrypt', 'sign', 'verify', + 'DecryptionError', 'VerificationError', 'CryptoError'] + +if __name__ == '__main__': + print('Running doctests 1000x or until failure') + import doctest + + for count in range(1000): + (failures, tests) = doctest.testmod() + if failures: + break + + if count and count % 100 == 0: + print('%i times' % count) + + print('Doctests done') diff --git a/src/rsa/prime.py b/src/rsa/prime.py new file mode 100644 index 00000000..7422eb1d --- /dev/null +++ b/src/rsa/prime.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Numerical functions related to primes. + +Implementation based on the book Algorithm Design by Michael T. Goodrich and +Roberto Tamassia, 2002. +''' + +__all__ = [ 'getprime', 'are_relatively_prime'] + +import rsa.randnum + +def gcd(p, q): + '''Returns the greatest common divisor of p and q + + >>> gcd(48, 180) + 12 + ''' + + while q != 0: + if p < q: (p,q) = (q,p) + (p,q) = (q, p % q) + return p + + +def jacobi(a, b): + '''Calculates the value of the Jacobi symbol (a/b) where both a and b are + positive integers, and b is odd + + :returns: -1, 0 or 1 + ''' + + assert a > 0 + assert b > 0 + + if a == 0: return 0 + result = 1 + while a > 1: + if a & 1: + if ((a-1)*(b-1) >> 2) & 1: + result = -result + a, b = b % a, a + else: + if (((b * b) - 1) >> 3) & 1: + result = -result + a >>= 1 + if a == 0: return 0 + return result + +def jacobi_witness(x, n): + '''Returns False if n is an Euler pseudo-prime with base x, and + True otherwise. + ''' + + j = jacobi(x, n) % n + + f = pow(x, n >> 1, n) + + if j == f: return False + return True + +def randomized_primality_testing(n, k): + '''Calculates whether n is composite (which is always correct) or + prime (which is incorrect with error probability 2**-k) + + Returns False if the number is composite, and True if it's + probably prime. + ''' + + # 50% of Jacobi-witnesses can report compositness of non-prime numbers + + # The implemented algorithm using the Jacobi witness function has error + # probability q <= 0.5, according to Goodrich et. al + # + # q = 0.5 + # t = int(math.ceil(k / log(1 / q, 2))) + # So t = k / log(2, 2) = k / 1 = k + # this means we can use range(k) rather than range(t) + + for _ in range(k): + x = rsa.randnum.randint(n-1) + if jacobi_witness(x, n): return False + + return True + +def is_prime(number): + '''Returns True if the number is prime, and False otherwise. + + >>> is_prime(42) + False + >>> is_prime(41) + True + ''' + + return randomized_primality_testing(number, 6) + +def getprime(nbits): + '''Returns a prime number that can be stored in 'nbits' bits. + + >>> p = getprime(128) + >>> is_prime(p-1) + False + >>> is_prime(p) + True + >>> is_prime(p+1) + False + + >>> from rsa import common + >>> common.bit_size(p) == 128 + True + + ''' + + while True: + integer = rsa.randnum.read_random_int(nbits) + + # Make sure it's odd + integer |= 1 + + # Test for primeness + if is_prime(integer): + return integer + + # Retry if not prime + + +def are_relatively_prime(a, b): + '''Returns True if a and b are relatively prime, and False if they + are not. + + >>> are_relatively_prime(2, 3) + 1 + >>> are_relatively_prime(2, 4) + 0 + ''' + + d = gcd(a, b) + return (d == 1) + +if __name__ == '__main__': + print('Running doctests 1000x or until failure') + import doctest + + for count in range(1000): + (failures, tests) = doctest.testmod() + if failures: + break + + if count and count % 100 == 0: + print('%i times' % count) + + print('Doctests done') diff --git a/src/rsa/randnum.py b/src/rsa/randnum.py new file mode 100644 index 00000000..0e782744 --- /dev/null +++ b/src/rsa/randnum.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Functions for generating random numbers.''' + +# Source inspired by code by Yesudeep Mangalapilly + +import os + +from rsa import common, transform +from rsa._compat import byte + +def read_random_bits(nbits): + '''Reads 'nbits' random bits. + + If nbits isn't a whole number of bytes, an extra byte will be appended with + only the lower bits set. + ''' + + nbytes, rbits = divmod(nbits, 8) + + # Get the random bytes + randomdata = os.urandom(nbytes) + + # Add the remaining random bits + if rbits > 0: + randomvalue = ord(os.urandom(1)) + randomvalue >>= (8 - rbits) + randomdata = byte(randomvalue) + randomdata + + return randomdata + + +def read_random_int(nbits): + '''Reads a random integer of approximately nbits bits. + ''' + + randomdata = read_random_bits(nbits) + value = transform.bytes2int(randomdata) + + # Ensure that the number is large enough to just fill out the required + # number of bits. + value |= 1 << (nbits - 1) + + return value + +def randint(maxvalue): + '''Returns a random integer x with 1 <= x <= maxvalue + + May take a very long time in specific situations. If maxvalue needs N bits + to store, the closer maxvalue is to (2 ** N) - 1, the faster this function + is. + ''' + + bit_size = common.bit_size(maxvalue) + + tries = 0 + while True: + value = read_random_int(bit_size) + if value <= maxvalue: + break + + if tries and tries % 10 == 0: + # After a lot of tries to get the right number of bits but still + # smaller than maxvalue, decrease the number of bits by 1. That'll + # dramatically increase the chances to get a large enough number. + bit_size -= 1 + tries += 1 + + return value + + diff --git a/src/rsa/transform.py b/src/rsa/transform.py new file mode 100644 index 00000000..c740b2d2 --- /dev/null +++ b/src/rsa/transform.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Data transformation functions. + +From bytes to a number, number to bytes, etc. +''' + +from __future__ import absolute_import + +try: + # We'll use psyco if available on 32-bit architectures to speed up code. + # Using psyco (if available) cuts down the execution time on Python 2.5 + # at least by half. + import psyco + psyco.full() +except ImportError: + pass + +import binascii +from struct import pack +from rsa import common +from rsa._compat import is_integer, b, byte, get_word_alignment, ZERO_BYTE, EMPTY_BYTE + + +def bytes2int(raw_bytes): + r'''Converts a list of bytes or an 8-bit string to an integer. + + When using unicode strings, encode it to some encoding like UTF8 first. + + >>> (((128 * 256) + 64) * 256) + 15 + 8405007 + >>> bytes2int('\x80@\x0f') + 8405007 + + ''' + + return int(binascii.hexlify(raw_bytes), 16) + + +def _int2bytes(number, block_size=None): + r'''Converts a number to a string of bytes. + + Usage:: + + >>> _int2bytes(123456789) + '\x07[\xcd\x15' + >>> bytes2int(_int2bytes(123456789)) + 123456789 + + >>> _int2bytes(123456789, 6) + '\x00\x00\x07[\xcd\x15' + >>> bytes2int(_int2bytes(123456789, 128)) + 123456789 + + >>> _int2bytes(123456789, 3) + Traceback (most recent call last): + ... + OverflowError: Needed 4 bytes for number, but block size is 3 + + @param number: the number to convert + @param block_size: the number of bytes to output. If the number encoded to + bytes is less than this, the block will be zero-padded. When not given, + the returned block is not padded. + + @throws OverflowError when block_size is given and the number takes up more + bytes than fit into the block. + ''' + # Type checking + if not is_integer(number): + raise TypeError("You must pass an integer for 'number', not %s" % + number.__class__) + + if number < 0: + raise ValueError('Negative numbers cannot be used: %i' % number) + + # Do some bounds checking + if number == 0: + needed_bytes = 1 + raw_bytes = [ZERO_BYTE] + else: + needed_bytes = common.byte_size(number) + raw_bytes = [] + + # You cannot compare None > 0 in Python 3x. It will fail with a TypeError. + if block_size and block_size > 0: + if needed_bytes > block_size: + raise OverflowError('Needed %i bytes for number, but block size ' + 'is %i' % (needed_bytes, block_size)) + + # Convert the number to bytes. + while number > 0: + raw_bytes.insert(0, byte(number & 0xFF)) + number >>= 8 + + # Pad with zeroes to fill the block + if block_size and block_size > 0: + padding = (block_size - needed_bytes) * ZERO_BYTE + else: + padding = EMPTY_BYTE + + return padding + EMPTY_BYTE.join(raw_bytes) + + +def bytes_leading(raw_bytes, needle=ZERO_BYTE): + ''' + Finds the number of prefixed byte occurrences in the haystack. + + Useful when you want to deal with padding. + + :param raw_bytes: + Raw bytes. + :param needle: + The byte to count. Default \000. + :returns: + The number of leading needle bytes. + ''' + leading = 0 + # Indexing keeps compatibility between Python 2.x and Python 3.x + _byte = needle[0] + for x in raw_bytes: + if x == _byte: + leading += 1 + else: + break + return leading + + +def int2bytes(number, fill_size=None, chunk_size=None, overflow=False): + ''' + Convert an unsigned integer to bytes (base-256 representation):: + + Does not preserve leading zeros if you don't specify a chunk size or + fill size. + + .. NOTE: + You must not specify both fill_size and chunk_size. Only one + of them is allowed. + + :param number: + Integer value + :param fill_size: + If the optional fill size is given the length of the resulting + byte string is expected to be the fill size and will be padded + with prefix zero bytes to satisfy that length. + :param chunk_size: + If optional chunk size is given and greater than zero, pad the front of + the byte string with binary zeros so that the length is a multiple of + ``chunk_size``. + :param overflow: + ``False`` (default). If this is ``True``, no ``OverflowError`` + will be raised when the fill_size is shorter than the length + of the generated byte sequence. Instead the byte sequence will + be returned as is. + :returns: + Raw bytes (base-256 representation). + :raises: + ``OverflowError`` when fill_size is given and the number takes up more + bytes than fit into the block. This requires the ``overflow`` + argument to this function to be set to ``False`` otherwise, no + error will be raised. + ''' + if number < 0: + raise ValueError("Number must be an unsigned integer: %d" % number) + + if fill_size and chunk_size: + raise ValueError("You can either fill or pad chunks, but not both") + + # Ensure these are integers. + number & 1 + + raw_bytes = b('') + + # Pack the integer one machine word at a time into bytes. + num = number + word_bits, _, max_uint, pack_type = get_word_alignment(num) + pack_format = ">%s" % pack_type + while num > 0: + raw_bytes = pack(pack_format, num & max_uint) + raw_bytes + num >>= word_bits + # Obtain the index of the first non-zero byte. + zero_leading = bytes_leading(raw_bytes) + if number == 0: + raw_bytes = ZERO_BYTE + # De-padding. + raw_bytes = raw_bytes[zero_leading:] + + length = len(raw_bytes) + if fill_size and fill_size > 0: + if not overflow and length > fill_size: + raise OverflowError( + "Need %d bytes for number, but fill size is %d" % + (length, fill_size) + ) + raw_bytes = raw_bytes.rjust(fill_size, ZERO_BYTE) + elif chunk_size and chunk_size > 0: + remainder = length % chunk_size + if remainder: + padding_size = chunk_size - remainder + raw_bytes = raw_bytes.rjust(length + padding_size, ZERO_BYTE) + return raw_bytes + + +if __name__ == '__main__': + import doctest + doctest.testmod() + diff --git a/src/rsa/util.py b/src/rsa/util.py new file mode 100644 index 00000000..5bbb70be --- /dev/null +++ b/src/rsa/util.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''Utility functions.''' + +from __future__ import with_statement, print_function + +import sys +from optparse import OptionParser + +import rsa.key + +def private_to_public(): + '''Reads a private key and outputs the corresponding public key.''' + + # Parse the CLI options + parser = OptionParser(usage='usage: %prog [options]', + description='Reads a private key and outputs the ' + 'corresponding public key. Both private and public keys use ' + 'the format described in PKCS#1 v1.5') + + parser.add_option('-i', '--input', dest='infilename', type='string', + help='Input filename. Reads from stdin if not specified') + parser.add_option('-o', '--output', dest='outfilename', type='string', + help='Output filename. Writes to stdout of not specified') + + parser.add_option('--inform', dest='inform', + help='key format of input - default PEM', + choices=('PEM', 'DER'), default='PEM') + + parser.add_option('--outform', dest='outform', + help='key format of output - default PEM', + choices=('PEM', 'DER'), default='PEM') + + (cli, cli_args) = parser.parse_args(sys.argv) + + # Read the input data + if cli.infilename: + print('Reading private key from %s in %s format' % \ + (cli.infilename, cli.inform), file=sys.stderr) + with open(cli.infilename, 'rb') as infile: + in_data = infile.read() + else: + print('Reading private key from stdin in %s format' % cli.inform, + file=sys.stderr) + in_data = sys.stdin.read().encode('ascii') + + assert type(in_data) == bytes, type(in_data) + + + # Take the public fields and create a public key + priv_key = rsa.key.PrivateKey.load_pkcs1(in_data, cli.inform) + pub_key = rsa.key.PublicKey(priv_key.n, priv_key.e) + + # Save to the output file + out_data = pub_key.save_pkcs1(cli.outform) + + if cli.outfilename: + print('Writing public key to %s in %s format' % \ + (cli.outfilename, cli.outform), file=sys.stderr) + with open(cli.outfilename, 'wb') as outfile: + outfile.write(out_data) + else: + print('Writing public key to stdout in %s format' % cli.outform, + file=sys.stderr) + sys.stdout.write(out_data.decode('ascii')) + + diff --git a/src/rsa/varblock.py b/src/rsa/varblock.py new file mode 100644 index 00000000..c7d96ae6 --- /dev/null +++ b/src/rsa/varblock.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2011 Sybren A. Stüvel +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +'''VARBLOCK file support + +The VARBLOCK file format is as follows, where || denotes byte concatenation: + + FILE := VERSION || BLOCK || BLOCK ... + + BLOCK := LENGTH || DATA + + LENGTH := varint-encoded length of the subsequent data. Varint comes from + Google Protobuf, and encodes an integer into a variable number of bytes. + Each byte uses the 7 lowest bits to encode the value. The highest bit set + to 1 indicates the next byte is also part of the varint. The last byte will + have this bit set to 0. + +This file format is called the VARBLOCK format, in line with the varint format +used to denote the block sizes. + +''' + +from rsa._compat import byte, b + + +ZERO_BYTE = b('\x00') +VARBLOCK_VERSION = 1 + +def read_varint(infile): + '''Reads a varint from the file. + + When the first byte to be read indicates EOF, (0, 0) is returned. When an + EOF occurs when at least one byte has been read, an EOFError exception is + raised. + + @param infile: the file-like object to read from. It should have a read() + method. + @returns (varint, length), the read varint and the number of read bytes. + ''' + + varint = 0 + read_bytes = 0 + + while True: + char = infile.read(1) + if len(char) == 0: + if read_bytes == 0: + return (0, 0) + raise EOFError('EOF while reading varint, value is %i so far' % + varint) + + byte = ord(char) + varint += (byte & 0x7F) << (7 * read_bytes) + + read_bytes += 1 + + if not byte & 0x80: + return (varint, read_bytes) + + +def write_varint(outfile, value): + '''Writes a varint to a file. + + @param outfile: the file-like object to write to. It should have a write() + method. + @returns the number of written bytes. + ''' + + # there is a big difference between 'write the value 0' (this case) and + # 'there is nothing left to write' (the false-case of the while loop) + + if value == 0: + outfile.write(ZERO_BYTE) + return 1 + + written_bytes = 0 + while value > 0: + to_write = value & 0x7f + value = value >> 7 + + if value > 0: + to_write |= 0x80 + + outfile.write(byte(to_write)) + written_bytes += 1 + + return written_bytes + + +def yield_varblocks(infile): + '''Generator, yields each block in the input file. + + @param infile: file to read, is expected to have the VARBLOCK format as + described in the module's docstring. + @yields the contents of each block. + ''' + + # Check the version number + first_char = infile.read(1) + if len(first_char) == 0: + raise EOFError('Unable to read VARBLOCK version number') + + version = ord(first_char) + if version != VARBLOCK_VERSION: + raise ValueError('VARBLOCK version %i not supported' % version) + + while True: + (block_size, read_bytes) = read_varint(infile) + + # EOF at block boundary, that's fine. + if read_bytes == 0 and block_size == 0: + break + + block = infile.read(block_size) + + read_size = len(block) + if read_size != block_size: + raise EOFError('Block size is %i, but could read only %i bytes' % + (block_size, read_size)) + + yield block + + +def yield_fixedblocks(infile, blocksize): + '''Generator, yields each block of ``blocksize`` bytes in the input file. + + :param infile: file to read and separate in blocks. + :returns: a generator that yields the contents of each block + ''' + + while True: + block = infile.read(blocksize) + + read_bytes = len(block) + if read_bytes == 0: + break + + yield block + + if read_bytes < blocksize: + break +