[AVTCORE] AD review: draft-ietf-avtcore-srtp-aes-gcm

Richard Barnes <rlb@ipv.sx> Sun, 03 November 2013 00:13 UTC

Return-Path: <rlb@ipv.sx>
X-Original-To: avt@ietfa.amsl.com
Delivered-To: avt@ietfa.amsl.com
Received: from localhost (localhost [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id BC22A21E8131 for <avt@ietfa.amsl.com>; Sat, 2 Nov 2013 17:13:26 -0700 (PDT)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -2.586
X-Spam-Level:
X-Spam-Status: No, score=-2.586 tagged_above=-999 required=5 tests=[AWL=-0.210, BAYES_00=-2.599, FM_FORGED_GMAIL=0.622, HTML_MESSAGE=0.001, J_CHICKENPOX_42=0.6, RCVD_IN_DNSWL_LOW=-1]
Received: from mail.ietf.org ([12.22.58.30]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id PYtI08wDeCbb for <avt@ietfa.amsl.com>; Sat, 2 Nov 2013 17:13:21 -0700 (PDT)
Received: from mail-ob0-f173.google.com (mail-ob0-f173.google.com [209.85.214.173]) by ietfa.amsl.com (Postfix) with ESMTP id CFA2921E8139 for <avt@ietf.org>; Sat, 2 Nov 2013 17:13:20 -0700 (PDT)
Received: by mail-ob0-f173.google.com with SMTP id gq1so5877576obb.18 for <avt@ietf.org>; Sat, 02 Nov 2013 17:13:20 -0700 (PDT)
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:date:message-id:subject:from:to :content-type; bh=MU+xRRmGy2gz9LSBzFfFSzGnMAr4uVvINu9Sa8bvC94=; b=iUu2nX3i7LdPqFHy8aAVTaCXAcMabXeNEs2e0utOSPi6VBHgvraX3W8bl6zkJD2xxh WSFbql1x2RK1IOZnh23/1Uowg0JJSU1clksH1phFYrmfZnA7iExtZNvrA/fNlNbmMOY7 mG3AbRadHCd0BhX7HZv7tqzg/36lLaTsUFSqegz3QOPs54Kxni5Gf+pDN2fJRgu2GjoY 8xX0FiLkOGDx0QpnGiJig9CdLzgfvvZuIZPgM4djrbaPoL523KuYWp1Fyq01vkIwOSzO YNOltsNfzot83J8OfwFqMLNL2/5CwraQGTxW29Lzc0u2ItEdk5wIljh9rtmapeJbrEr1 WYWQ==
X-Gm-Message-State: ALoCoQnPCH1oDbon6AaerGx4NVXSPHrk4ZRarC74veorSfL4yH1lUXvissIvvRuVUgWuQHlRetcl
MIME-Version: 1.0
X-Received: by 10.60.51.196 with SMTP id m4mr7884844oeo.1.1383437600036; Sat, 02 Nov 2013 17:13:20 -0700 (PDT)
Received: by 10.60.31.74 with HTTP; Sat, 2 Nov 2013 17:13:19 -0700 (PDT)
Date: Sat, 2 Nov 2013 17:13:19 -0700
Message-ID: <CAL02cgTEppKoKohqJLVDs41yBSD=UfayiTuQ1EsTCt-ZzTVkNw@mail.gmail.com>
From: Richard Barnes <rlb@ipv.sx>
To: draft-ietf-avtcore-srtp-aes-gcm@tools.ietf.org, avt@ietf.org
Content-Type: multipart/alternative; boundary=001a11c30920b82bd204ea3aaad4
Subject: [AVTCORE] AD review: draft-ietf-avtcore-srtp-aes-gcm
X-BeenThere: avt@ietf.org
X-Mailman-Version: 2.1.12
Precedence: list
List-Id: Audio/Video Transport Core Maintenance <avt.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/avt>, <mailto:avt-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/avt>
List-Post: <mailto:avt@ietf.org>
List-Help: <mailto:avt-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/avt>, <mailto:avt-request@ietf.org?subject=subscribe>
X-List-Received-Date: Sun, 03 Nov 2013 00:13:26 -0000

I have reviewed this draft in preparation for IETF Last Call and IESG
processing.  Overall, it's in very good shape.  Just have a few minor
questions and editorial suggestions:

MINOR
-----

S5.2.1
It's a little confusing that the tag size an input to CCM, but "fixed by
the algorithm" for GCM, since in both cases (GCM/CCM), the length of the
tag is fixed by negotiated algorithm (Table 1/8).  Perhaps rephrase to
clarify that the CCM mode requires tag length to be explicitly input to the
algorithm, whereas with GCM, the tag is simply truncated.

S5.3
Why is the guidance for "MUST not decrypt before verify" different for GCM
vs. CCM?

S6
Why is this document re-defining the key streams from the GCM and CCM
specs?  Shouldn't this just say how the intial counter or counter sequence
is formed?

S13.1
In this section, _8 is SHOULD, and _12 is MAY.  What about AEAD_AES_128_GCM
itself?  Is that a MUST implement?  It seems like there should be one.
Likewise for S13.2 and CCM.



EDITORIAL
---------

S5.2.1
The document mostly uses "octet", but there are three instances of "byte",
all in the table in this section.  Probably good to use "octet" there as
well, for clarity.

S6
The code here is *almost* valid python, but not quite.  In case it's
helpful, a revised version in Python is below (since I had time on the
plane on the way to the IETF meeting :) ).

S14.1, 3rd bullet
It might be helpful to note that although the counter is reset for every
packet, IV uniqueness is ensured by the inclusion of SSRC/ROC/SEQ or SRTCP
Index in the IV (and the accompanying management of these parametesr.  This
is stated above, but if restated here, would help justify the last two
bullets.






-----BEGIN srtp-aead.py-----
#!/usr/bin/env python

from struct import pack
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto.Util.number import long_to_bytes, bytes_to_long

# Parameters
Plaintext = "\x00" * 48 # Must be all zeros for CTR-based validation to work
IV = "iv" * 6
Encryption_key = "sixteen byte key"


# Helper functions required by the algorithms in the draft
def AES_ENC( data="", key="" ):
    assert( len(data) == 16 )
    assert( len(key) in [16, 24, 32] )
    return AES.new(key).encrypt(data)

def TO_BYTES( counter, n ):
    return long_to_bytes(counter, n)


# For each algorithm, we generate the key stream with the algorithm
# in the draft, and compare to PyCrypto's CTR implementation.  (We
# get the key stream from the CTR mode by encrypting the all-zero
# octet string.)

# GCM
def GCM_keystream( Plaintext, IV, Encryption_key ):
    assert( len(Plaintext)  <= (2**36) - 32 ) ## measured in octets
    key_stream = ""
    block_counter = 1
    first_key_block = AES_ENC( data = IV + TO_BYTES(block_counter,4),
                               key=Encryption_key        )
    while len(key_stream) < len(Plaintext):
        block_counter = block_counter + 1
        key_block = AES_ENC( data=IV + TO_BYTES(block_counter,4),
                             key=Encryption_key        )
        key_stream  = key_stream + key_block
    key_stream = key_stream[:len(Plaintext)]
    return (first_key_block, key_stream )

Key_stream_G = GCM_keystream( Plaintext, IV, Encryption_key )
Key_stream_G = Key_stream_G[0] + Key_stream_G[1]
ivn = bytes_to_long( IV + ("\x00" * 4) )
ctr = Counter.new(128, initial_value = ivn+1)
Ciphertext_G = AES.new(Encryption_key, AES.MODE_CTR,
counter=ctr).encrypt(Plaintext)


# CCM
def CCM_keystream( Plaintext, IV, Encryption_key ):
    assert( len(Plaintext) <= (2**28)-16 ) ## measured in octets
    key_stream = ""
    block_counter = 0
    first_key_block = AES_ENC( data='\x02' + IV + TO_BYTES(block_counter,3),
                               key=Encryption_key            )
    while len(key_stream)<len(Plaintext):
        block_counter = block_counter + 1
        key_block = AES_ENC( data='\x02' + IV + TO_BYTES(block_counter,3),
                             key=Encryption_key            )
        key_stream  = key_stream + key_block
    key_stream = key_stream[:len(Plaintext)]
    return (first_key_block, key_stream )

Key_stream_C = CCM_keystream( Plaintext, IV, Encryption_key )
Key_stream_C = Key_stream_C[0] + Key_stream_C[1]
ivn = bytes_to_long( '\x02' + IV + ("\x00" * 3) )
ctr = Counter.new(128, initial_value = ivn)
Ciphertext_C = AES.new(Encryption_key, AES.MODE_CTR,
counter=ctr).encrypt(Plaintext)


# Print out results.  KSg and KSg' should match, and KSc/KSc'
print "P:    {}".format(Plaintext.encode("hex"))
print "IV:   {}".format(IV.encode("hex"))
print "K:    {}".format(Encryption_key.encode("hex"))
print "KSg:  {}".format(Key_stream_G.encode("hex"))
print "KSg': {}".format(Ciphertext_G.encode("hex"))
print "KSc:  {}".format(Key_stream_C.encode("hex"))
print "KSc': {}".format(Ciphertext_C.encode("hex"))
-----END srtp-aead.py-----