[CalendarServer-changes] [10598] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Tue Jan 29 10:29:58 PST 2013


Revision: 10598
          http://trac.calendarserver.org//changeset/10598
Author:   cdaboo at apple.com
Date:     2013-01-29 10:29:58 -0800 (Tue, 29 Jan 2013)
Log Message:
-----------
iSchedule DKIM updates.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/scheduling/ischedule/delivery.py
    CalendarServer/trunk/twistedcaldav/scheduling/ischedule/dkim.py
    CalendarServer/trunk/twistedcaldav/scheduling/ischedule/resource.py
    CalendarServer/trunk/twistedcaldav/scheduling/ischedule/test/test_dkim.py
    CalendarServer/trunk/twistedcaldav/scheduling/ischedule/xml.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py

Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischedule/delivery.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischedule/delivery.py	2013-01-29 18:28:17 UTC (rev 10597)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischedule/delivery.py	2013-01-29 18:29:58 UTC (rev 10598)
@@ -359,7 +359,6 @@
 
         self.headers = Headers()
         self.headers.setHeader("Host", utf8String(host + ":%s" % (port,)))
-        self.sign_headers.append("Host")
 
         # The Originator must be the ORGANIZER (for a request) or ATTENDEE (for a reply)
         originator = self.scheduler.organizer.cuaddr if self.scheduler.isiTIPRequest else self.scheduler.attendee
@@ -370,8 +369,8 @@
         for recipient in self.recipients:
             self.headers.addRawHeader("Recipient", utf8String(recipient.cuaddr))
 
-        # Remember to "over sign" the Recipient header
-        self.sign_headers.append("Recipient+")
+        # Only one Recipient header as they get concatenated in ischedule-relaxed canonicalization
+        self.sign_headers.append("Recipient")
 
         self._doAuthentication()
 

Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischedule/dkim.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischedule/dkim.py	2013-01-29 18:28:17 UTC (rev 10597)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischedule/dkim.py	2013-01-29 18:29:58 UTC (rev 10598)
@@ -33,8 +33,6 @@
 from Crypto.Signature import PKCS1_v1_5
 
 import base64
-import binascii
-import collections
 import hashlib
 import os
 import textwrap
@@ -61,9 +59,9 @@
 ISCHEDULE_VERSION = "iSchedule-Version"
 ISCHEDULE_VERSION_VALUE = "1.0"
 ISCHEDULE_MESSAGE_ID = "iSchedule-Message-ID"
+ISCHEDULE_CAPABILITIES = "iSchedule-Capabilities"
 
 
-
 class DKIMUtils(object):
     """
     Some useful functions.
@@ -207,7 +205,18 @@
 
 
     @staticmethod
-    def canonicalizeHeader(name, value, remove_b=None):
+    def canonicalizeHeader(name, value, dkim_tags=None, method="ischedule-relaxed"):
+
+        if method == "relaxed":
+            return DKIMUtils.relaxedHeader(name, value, dkim_tags)
+        elif method == "ischedule-relaxed":
+            return DKIMUtils.ischeduleHeader(name, value, dkim_tags)
+        else:
+            assert "Invalid header canonicalization method: %s" % (method,)
+
+
+    @staticmethod
+    def relaxedHeader(name, value, dkim_tags=None):
         """
         Canonicalize the header using "relaxed" method. Optionally remove the b= value from
         any DKIM-Signature present.
@@ -220,25 +229,70 @@
         @type name: C{str}
         @param value: header value
         @type value: C{str}
-        @param remove_b: the b= value to remove, or C{None} if no removal needed
-        @type remove_b: C{str} or C{None}
+        @param dkim_tags: the extracted DKIM tags, or C{None} if no removal needed
+        @type dkim_tags: C{dict} or C{None}
         """
 
+        # Special case DKIM-Signature: remove the b= value for signature
+        name = name.lower()
+        if dkim_tags is not None and name == DKIM_SIGNATURE.lower():
+            value = DKIMUtils.canonicalizeDKIMHeaderFields(value, dkim_tags)
+
         # Basic relaxed behavior
-        name = name.lower()
         value = " ".join(value.split())
 
+        crlf = "" if name == DKIM_SIGNATURE.lower() else "\r\n"
+        return "%s:%s%s" % (name, value, crlf)
+
+
+    @staticmethod
+    def ischeduleHeader(name, value, dkim_tags=None):
+        """
+        Canonicalize the header using "ischedule-relaxed" method. Optionally remove the b= value from
+        any DKIM-Signature present.
+
+        FIXME: this needs to be smarter about where valid WSP can occur in a header. Right now it will
+        blindly collapse all runs of SP/HTAB into a single SP. That could be wrong if a legitimate sequence of
+        SP/HTAB occurs in a header value.
+
+        @param name: header name
+        @type name: C{str}
+        @param value: header value
+        @type value: C{str}
+        @param dkim_tags: the extracted DKIM tags, or C{None} if no removal needed
+        @type dkim_tags: C{dict} or C{None}
+        """
+
         # Special case DKIM-Signature: remove the b= value for signature
-        if remove_b is not None and name == DKIM_SIGNATURE.lower():
-            pos = value.find(remove_b)
-            value = value[:pos] + value[pos + len(remove_b):]
-            value = " ".join(value.split())
+        name = name.lower()
+        if dkim_tags is not None and name == DKIM_SIGNATURE.lower():
+            value = DKIMUtils.canonicalizeDKIMHeaderFields(value, dkim_tags)
 
+        # Basic relaxed behavior
+        value = " ".join(value.split())
+        value = value.replace(" ,", ",")
+        value = value.replace(", ", ",")
+
         crlf = "" if name == DKIM_SIGNATURE.lower() else "\r\n"
         return "%s:%s%s" % (name, value, crlf)
 
 
     @staticmethod
+    def canonicalizeDKIMHeaderFields(value, dkim_tags):
+        """
+        DKIM-Signature b= value needs to be stripped.
+
+        @param value: header value to process
+        @type value: C{str}
+        """
+
+        pos = value.find(dkim_tags["b"])
+        value = value[:pos] + value[pos + len(dkim_tags["b"]):]
+        value = " ".join(value.split())
+        return value
+
+
+    @staticmethod
     def canonicalizeBody(data):
         if not data.endswith("\r\n"):
             data += "\r\n"
@@ -402,13 +456,11 @@
         sign_headers = []
         raw = dict([(name.lower(), values) for name, values in self.headers.getAllRawHeaders()])
         for name in self.sign_headers:
-            oversign = name[-1] == "+"
-            name = name.rstrip("+")
-            for value in reversed(raw.get(name.lower(), ())):
-                headers.append(DKIMUtils.canonicalizeHeader(name, value))
-                sign_headers.append(name)
-            if oversign:
-                sign_headers.append(name)
+            # ischedule-relaxed canonicalization requires headers with the same name concatenated
+            # with a comma in between
+            value = ",".join(raw.get(name.lower(), ()))
+            headers.append(DKIMUtils.canonicalizeHeader(name, value))
+            sign_headers.append(name)
 
         # Generate the DKIM header tags we care about
         dkim_tags = []
@@ -419,8 +471,7 @@
         dkim_tags.append(("x", self.expire,))
         dkim_tags.append(("a", self.algorithm,))
         dkim_tags.append(("q", ":".join(self.keyMethods),))
-        dkim_tags.append(("http", base64.encodestring("%s:%s" % (self.method, self.uri,)).strip()))
-        dkim_tags.append(("c", "relaxed/simple",))
+        dkim_tags.append(("c", "ischedule-relaxed/simple",))
         dkim_tags.append(("h", ":".join(sign_headers),))
         dkim_tags.append(("bh", (yield self.bodyHash()),))
         dkim_tags.append(("b", "",))
@@ -501,7 +552,7 @@
 
         # Do header verification
         try:
-            DKIMUtils.verify(headers, self.dkim_tags["b"], pubkey, self.hash_func)
+            DKIMUtils.verify(headers, self.dkim_tags["_b"], pubkey, self.hash_func)
         except ValueError:
             msg = "Could not verify signature"
             _debug_msg = """
@@ -524,7 +575,7 @@
         self.request.stream.doStartReading = None
         body = DKIMUtils.canonicalizeBody(data)
         bh = base64.b64encode(self.hash_method(body).digest())
-        if bh != self.dkim_tags["bh"]:
+        if bh != self.dkim_tags["_bh"]:
             msg = "Could not verify the DKIM body hash"
             _debug_msg = """
 DKIM-Signature:%s
@@ -566,7 +617,7 @@
         self.dkim_tags = DKIMUtils.extractTags(dkim)
 
         # Verify validity of tags
-        required_tags = ("v", "a", "b", "bh", "c", "d", "h", "s", "http",)
+        required_tags = ("v", "a", "b", "bh", "c", "d", "h", "s",)
         for tag in required_tags:
             if tag not in self.dkim_tags:
                 msg = "Missing DKIM-Signature tag: %s" % (tag,)
@@ -576,7 +627,7 @@
         check_values = {
             "v": ("1",),
             "a": (RSA1, RSA256,),
-            "c": ("relaxed", "relaxed/simple",),
+            "c": ("ischedule-relaxed", "ischedule-relaxed/simple",),
             "q": (Q_DNS, Q_HTTP, Q_PRIVATE,),
         }
         for tag, values in check_values.items():
@@ -594,6 +645,14 @@
                     log.debug("DKIM: " + msg)
                     raise DKIMVerificationError(msg)
 
+        # Check time stamp
+        if "t" in self.dkim_tags:
+            diff_time = self.time - int(self.dkim_tags["t"])
+            if diff_time < -360:
+                msg = "Signature time to far in the future: %d seconds" % (diff_time,)
+                log.debug("DKIM: " + msg)
+                raise DKIMVerificationError(msg)
+
         # Check expiration
         if "x" in self.dkim_tags:
             diff_time = self.time - int(self.dkim_tags["x"])
@@ -602,27 +661,9 @@
                 log.debug("DKIM: " + msg)
                 raise DKIMVerificationError(msg)
 
-        # Check HTTP method/request-uri
-        try:
-            http_tag = base64.decodestring(self.dkim_tags["http"])
-        except binascii.Error:
-            msg = "Tag: http is not valid base64"
-            log.debug("DKIM: " + msg)
-            raise DKIMVerificationError(msg)
-        try:
-            method, uri = http_tag.split(":", 1)
-        except ValueError:
-            msg = "Tag: base64-decoded http is not valid: %s" % (http_tag,)
-            log.debug("DKIM: " + msg)
-            raise DKIMVerificationError(msg)
-        if method != self.request.method:
-            msg = "Tag: http method does not match: %s" % (method,)
-            log.debug("DKIM: " + msg)
-            raise DKIMVerificationError(msg)
-        if uri != self.request.uri:
-            msg = "Tag: http request-URI does not match: %s" % (uri,)
-            log.debug("DKIM: " + msg)
-            raise DKIMVerificationError(msg)
+        # Base64 encoded tags might include WSP which we need to ignore
+        for tag in ("b", "bh",):
+            self.dkim_tags["_%s" % (tag,)] = "".join(self.dkim_tags[tag].split())
 
         # Some useful bits
         self.hash_method = DKIMUtils.hashlib_method(self.dkim_tags["a"])
@@ -636,26 +677,21 @@
         and return the expected signed data.
         """
 
-        # Extract all the expected signed headers taking into account the possibility of "over_counting"
-        # headers - a technique used to ensure headers cannot be added in transit
+        # Extract all the expected signed headers taking into account multiple occurrences of a header
+        # which get concatenated with a single comma in between.
         header_list = [hdr.strip() for hdr in self.dkim_tags["h"].split(":")]
-        header_counter = collections.defaultdict(int)
 
         headers = []
         for header in header_list:
             actual_headers = self.request.headers.getRawHeaders(header)
             if actual_headers:
-                try:
-                    headers.append((header, actual_headers[-1 - header_counter[header]],))
-                except IndexError:
-                    pass
-            header_counter[header] += 1
+                headers.append((header, ",".join(actual_headers),))
 
         # DKIM-Signature is always included at the end
         headers.append((DKIM_SIGNATURE, self.request.headers.getRawHeaders(DKIM_SIGNATURE)[0],))
 
         # Now canonicalize the values
-        return "".join([DKIMUtils.canonicalizeHeader(name, value, remove_b=self.dkim_tags["b"]) for name, value in headers])
+        return "".join([DKIMUtils.canonicalizeHeader(name, value, dkim_tags=self.dkim_tags) for name, value in headers])
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischedule/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischedule/resource.py	2013-01-29 18:28:17 UTC (rev 10597)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischedule/resource.py	2013-01-29 18:29:58 UTC (rev 10598)
@@ -30,6 +30,7 @@
 from twistedcaldav.scheduling.ischedule.scheduler import IScheduleScheduler
 from txdav.xml import element as davxml
 import twistedcaldav.scheduling.ischedule.xml  as ischedulexml
+from twistedcaldav.scheduling.ischedule.dkim import ISCHEDULE_CAPABILITIES
 
 __all__ = [
     "IScheduleInboxResource",
@@ -156,6 +157,7 @@
         result = ischedulexml.QueryResult(
 
             ischedulexml.Capabilities(
+                ischedulexml.Version.fromString(config.Scheduling.iSchedule.SerialNumber),
                 ischedulexml.Versions(
                     ischedulexml.Version.fromString("1.0"),
                 ),
@@ -194,7 +196,9 @@
                 ischedulexml.Administrator.fromString(request.unparseURL(params="", querystring="", fragment="")),
             ),
         )
-        return XMLResponse(responsecode.OK, result)
+        response = XMLResponse(responsecode.OK, result)
+        response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
+        return response
 
 
     @inlineCallbacks
@@ -218,7 +222,9 @@
             raise e
         else:
             yield txn.commit()
-        returnValue(result.response())
+        response = result.response()
+        response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
+        returnValue(response)
 
     ##
     # ACL

Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischedule/test/test_dkim.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischedule/test/test_dkim.py	2013-01-29 18:28:17 UTC (rev 10597)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischedule/test/test_dkim.py	2013-01-29 18:29:58 UTC (rev 10598)
@@ -149,7 +149,7 @@
 recipient:mailto:user02 at example.com
 content-type:%s
 ischedule-version:1.0
-dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], str(int(time.time())), str(int(time.time() + 3600)), algorithm, bodyhash)
+dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], str(int(time.time())), str(int(time.time() + 3600)), algorithm, bodyhash)
 
             result = request.generateSignature(sign_this)
 
@@ -183,7 +183,7 @@
 content-type:%s
 ischedule-version:1.0
 ischedule-message-id:%s
-dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], request.message_id, request.time, request.expire, algorithm, bodyhash)
+dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; c=ischedule-relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], request.message_id, request.time, request.expire, algorithm, bodyhash)
 
             self.assertEqual(result, sign_this)
 
@@ -211,14 +211,14 @@
 content-type:%s
 ischedule-version:1.0
 ischedule-message-id:%s
-dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], request.message_id, request.time, request.expire, algorithm, bodyhash)
+dkim-signature:v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; c=ischedule-relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=""".replace("\n", "\r\n") % (headers.getRawHeaders("Content-Type")[0], request.message_id, request.time, request.expire, algorithm, bodyhash)
             key = RSA.importKey(open(self.private_keyfile).read())
             signature = DKIMUtils.sign(sign_this, key, DKIMUtils.hash_func(algorithm))
 
             self.assertEqual(result, signature)
 
             # Make sure header is updated in the request
-            updated_header = "v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=%s" % (request.time, request.expire, algorithm, bodyhash, signature,)
+            updated_header = "v=1; d=example.com; s=dkim; t=%s; x=%s; a=%s; q=private-exchange:http/well-known:dns/txt; c=ischedule-relaxed/simple; h=Originator:Recipient:Content-Type:iSchedule-Version:iSchedule-Message-ID; bh=%s; b=%s" % (request.time, request.expire, algorithm, bodyhash, signature,)
             self.assertEqual(request.headers.getRawHeaders("DKIM-Signature")[0], updated_header)
 
             # Try to verify result using public key
@@ -254,24 +254,24 @@
 
             # More than one
             ((
-                ("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),
-                ("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha256; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),
+                ("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),
+                ("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha256; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),
             ), False,),
 
             # Valid
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), True,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha256; q=dns/txt; http=UE9TVDov; c=relaxed; h=Originator:Recipient; bh=abc; b=def"),), True,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; http=UE9TVDov; c=relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() + 30),)),), True,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), True,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha256; q=dns/txt; c=ischedule-relaxed; h=Originator:Recipient; bh=abc; b=def"),), True,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; c=ischedule-relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() + 30),)),), True,),
 
             # Invalid
-            ((("DKIM-Signature", "v=2; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), False,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha512; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), False,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/relaxed; h=Originator:Recipient; bh=abc; b=def"),), False,),
-            ((("DKIM-Signature", "v=1; d=example.com; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), False,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; http=UE9TVDov; c=relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; c=relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; http=UE9TVDovaXNjaGVkdWxl; c=relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
-            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; http=POST:/; c=relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
+            ((("DKIM-Signature", "v=2; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), False,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha512; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), False,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; c=ischedule-relaxed/relaxed; h=Originator:Recipient; bh=abc; b=def"),), False,),
+            ((("DKIM-Signature", "v=1; d=example.com; t=1234; a=rsa-sha1; q=dns/txt:http/well-known; c=ischedule-relaxed/simple; h=Originator:Recipient; bh=abc; b=def"),), False,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; c=ischedule-relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; c=ischedule-relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; c=ischedule-relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
+            ((("DKIM-Signature", "v=1; d=example.com; s=dkim; t=1234; x=%d; a=rsa-sha256; q=dns/txt; c=ischedule-relaxed; h=Originator:Recipient; bh=abc; b=def" % (int(time.time() - 30),)),), False,),
         )
 
         for headers, result in data:
@@ -291,17 +291,17 @@
         data = (
             ("Content-Type", " text/calendar  ; charset =  \"utf-8\"  ", "content-type:text/calendar ; charset = \"utf-8\"\r\n"),
             ("Originator", "  mailto:user01 at example.com  ", "originator:mailto:user01 at example.com\r\n"),
-            ("Recipient", "  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t  ", "recipient:mailto:user02 at example.com , mailto:user03 at example.com\r\n"),
+            ("Recipient", "  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t  ", "recipient:mailto:user02 at example.com,mailto:user03 at example.com\r\n"),
             ("iSchedule-Version", " 1.0 ", "ischedule-version:1.0\r\n"),
             (
                 "DKIM-Signature",
-                "  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def",
-                "dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; http=UE9TVDov; c=relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=",
+                "  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; c=ischedule-relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=a b  c; b=d ef",
+                "dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; c=ischedule-relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=a b c; b=",
             ),
             (
                 "DKIM-Signature",
-                "  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; b= def ; http=\tUE9TVDov   ; c=relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc",
-                "dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; b= ; http= UE9TVDov ; c=relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc",
+                "  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; b= def ; c=ischedule-relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=a\t bc",
+                "dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; b= ; c=ischedule-relaxed/simple; h=Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=a bc",
             ),
         )
 
@@ -310,7 +310,7 @@
             verifier = DKIMVerifier(request)
             if name == "DKIM-Signature":
                 verifier.processDKIMHeader()
-            canonicalized = DKIMUtils.canonicalizeHeader(name, value, remove_b=verifier.dkim_tags["b"] if name == "DKIM-Signature" else None)
+            canonicalized = DKIMUtils.canonicalizeHeader(name, value, verifier.dkim_tags if name == "DKIM-Signature" else None)
             self.assertEqual(canonicalized, result)
 
 
@@ -320,21 +320,21 @@
         """
 
         data = (
-            # Over count on Recipient
+            # Count on Recipient
             ("""Host:example.com
 Content-Type: text/calendar  ; charset =  "utf-8"
 Originator:  mailto:user01 at example.com
 Recipient:  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t
 iSchedule-Version: 1.0
-DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
+DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
 Cache-Control:no-cache
 Connection:close
 """,
             """content-type:text/calendar ; charset = "utf-8"
 originator:mailto:user01 at example.com
-recipient:mailto:user02 at example.com , mailto:user03 at example.com
+recipient:mailto:user02 at example.com,mailto:user03 at example.com
 ischedule-version:1.0
-dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b="""
+dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b="""
             ),
             # Exact count on Recipient
             ("""Host:example.com
@@ -343,51 +343,31 @@
 Recipient:  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t
 Recipient:\t\t  mailto:user04 at example.com
 iSchedule-Version: 1.0
-DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
+DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
 Cache-Control:no-cache
 Connection:close
 """,
             """content-type:text/calendar ; charset = "utf-8"
 originator:mailto:user01 at example.com
-recipient:mailto:user04 at example.com
-recipient:mailto:user02 at example.com , mailto:user03 at example.com
+recipient:mailto:user02 at example.com,mailto:user03 at example.com,mailto:user04 at example.com
 ischedule-version:1.0
-dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b="""
+dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b="""
             ),
-            # Under count on Recipient
-            ("""Host:example.com
-Content-Type: text/calendar  ; charset =  "utf-8"
-iSchedule-Version: 1.0
-Originator:  mailto:user01 at example.com
-Recipient:  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t
-Recipient:\t\t  mailto:user04 at example.com
-Recipient:\t\t  mailto:user05 at example.com
-DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
-Cache-Control:no-cache
-Connection:close
-""",
-            """content-type:text/calendar ; charset = "utf-8"
-originator:mailto:user01 at example.com
-recipient:mailto:user05 at example.com
-recipient:mailto:user04 at example.com
-ischedule-version:1.0
-dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b="""
-            ),
             # Re-ordered Content-Type
             ("""Host:example.com
 iSchedule-Version: 1.0
 Originator:  mailto:user01 at example.com
 Recipient:  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t
-DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
+DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
 Content-Type: text/calendar  ; charset =  "utf-8"
 Cache-Control:no-cache
 Connection:close
 """,
             """content-type:text/calendar ; charset = "utf-8"
 originator:mailto:user01 at example.com
-recipient:mailto:user02 at example.com , mailto:user03 at example.com
+recipient:mailto:user02 at example.com,mailto:user03 at example.com
 ischedule-version:1.0
-dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b="""
+dkim-signature:v=1; d=example.com; s = dkim; t = 1234; a=rsa-sha1; q=dns/txt:http/well-known ; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b="""
             ),
         )
 
@@ -411,7 +391,7 @@
 Content-Type: text/calendar  ; charset =  "utf-8"
 Originator:  mailto:user01 at example.com
 Recipient:  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t
-DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
+DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
 Cache-Control:no-cache
 Connection:close
 """,
@@ -423,7 +403,7 @@
 Content-Type: text/calendar  ; charset =  "utf-8"
 Originator:  mailto:user01 at example.com
 Recipient:  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t
-DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt\t\t; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
+DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt\t\t; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
 Cache-Control:no-cache
 Connection:close
 """,
@@ -435,7 +415,7 @@
 Content-Type: text/calendar  ; charset =  "utf-8"
 Originator:  mailto:user01 at example.com
 Recipient:  mailto:user02 at example.com  ,\t mailto:user03 at example.com\t\t
-DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; http=UE9TVDov; c=relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
+DKIM-Signature:  v=1;\t\t d=example.com; s = dkim; t\t=\t1234; a=rsa-sha1; \t\tq=dns/txt:http/well-known\t\t; c=ischedule-relaxed/simple; h=Content-Type:Originator:Recipient:Recipient:iSchedule-Version:iSchedule-Message-ID; bh=abc; b=def
 Cache-Control:no-cache
 Connection:close
 """,
@@ -574,7 +554,7 @@
             manipulate_request=lambda request: setattr(request, "stream", MemoryStream("BEGIN:DATA\n")),
         )
 
-        # Valid - extra header no over sign
+        # Invalid - extra header
         yield _verify(
             """Host:example.com
 Content-Type: text/calendar  ; charset =  "utf-8"
@@ -587,11 +567,11 @@
 END:DATA
 """,
             [DKIMUtils.extractTags("v=DKIM1; p=%s" % (self.public_key_data,))],
-            True,
+            False,
             manipulate_request=lambda request: request.headers.getRawHeaders("Recipient").insert(0, "mailto:user04 at example.com"),
         )
 
-        # Valid - over sign header
+        # Valid - header
         yield _verify(
             """Host:example.com
 Content-Type: text/calendar  ; charset =  "utf-8"
@@ -604,8 +584,8 @@
 END:DATA
 """,
             [DKIMUtils.extractTags("v=DKIM1; p=%s" % (self.public_key_data,))],
-            False,
-            sign_headers=("Originator", "Recipient", "Recipient", "Content-Type",),
+            True,
+            sign_headers=("Originator", "Recipient", "Content-Type",),
         )
 
         # Invalid - over sign header extra header
@@ -622,7 +602,7 @@
 """,
             [DKIMUtils.extractTags("v=DKIM1; p=%s" % (self.public_key_data,))],
             False,
-            sign_headers=("Originator", "Recipient", "Recipient", "Content-Type",),
+            sign_headers=("Originator", "Recipient", "Content-Type",),
             manipulate_request=lambda request: request.headers.addRawHeader("Recipient", ("mailto:user04 at example.com",))
         )
 

Modified: CalendarServer/trunk/twistedcaldav/scheduling/ischedule/xml.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/ischedule/xml.py	2013-01-29 18:28:17 UTC (rev 10597)
+++ CalendarServer/trunk/twistedcaldav/scheduling/ischedule/xml.py	2013-01-29 18:29:58 UTC (rev 10598)
@@ -48,6 +48,7 @@
     name = "capabilities"
 
     allowed_children = {
+        (ischedule_namespace, "serial-number"): (1, 1),
         (ischedule_namespace, "versions"): (1, 1),
         (ischedule_namespace, "scheduling-messages"): (1, 1),
         (ischedule_namespace, "calendar-data-types"): (1, 1),
@@ -64,6 +65,13 @@
 
 
 @registerElement
+class SerialNumber (WebDAVTextElement):
+    namespace = ischedule_namespace
+    name = "serial-number"
+
+
+
+ at registerElement
 class Versions (WebDAVElement):
     namespace = ischedule_namespace
     name = "versions"

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2013-01-29 18:28:17 UTC (rev 10597)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2013-01-29 18:29:58 UTC (rev 10598)
@@ -641,6 +641,7 @@
             "Enabled"          : False, # iSchedule protocol
             "AddressPatterns"  : [], # Reg-ex patterns to match iSchedule-able calendar user addresses
             "RemoteServers"    : "remoteservers.xml", # iSchedule server configurations
+            "SerialNumber"     : 1,  # Capabilities serial number
             "DNSDebug"         : "", # File where a fake Bind zone exists for creating fake DNS results
             "DKIM"             : {      # DKIM options
                 "Enabled"               : True, # DKIM signing/verification enabled
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130129/02334928/attachment-0001.html>


More information about the calendarserver-changes mailing list