[DNSOP] Any suggestion on what I'm doing that is stupid here on NSEC3?

Nicholas Weaver <nweaver@icsi.berkeley.edu> Wed, 12 February 2014 15:35 UTC

Return-Path: <nweaver@icsi.berkeley.edu>
X-Original-To: dnsop@ietfa.amsl.com
Delivered-To: dnsop@ietfa.amsl.com
Received: from localhost (ietfa.amsl.com []) by ietfa.amsl.com (Postfix) with ESMTP id 06F061A08DB for <dnsop@ietfa.amsl.com>; Wed, 12 Feb 2014 07:35:51 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -3.849
X-Spam-Status: No, score=-3.849 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, GB_I_LETTER=-2, J_CHICKENPOX_24=0.6, RP_MATCHES_RCVD=-0.548, SPF_PASS=-0.001] autolearn=ham
Received: from mail.ietf.org ([]) by localhost (ietfa.amsl.com []) (amavisd-new, port 10024) with ESMTP id wctoxlILTIvi for <dnsop@ietfa.amsl.com>; Wed, 12 Feb 2014 07:35:48 -0800 (PST)
Received: from rock.ICSI.Berkeley.EDU (rock.ICSI.Berkeley.EDU []) by ietfa.amsl.com (Postfix) with ESMTP id 87FE31A057E for <dnsop@ietf.org>; Wed, 12 Feb 2014 07:35:48 -0800 (PST)
Received: from localhost (localhost.localdomain []) by rock.ICSI.Berkeley.EDU (Postfix) with ESMTP id 08B472C403A for <dnsop@ietf.org>; Wed, 12 Feb 2014 07:35:48 -0800 (PST)
X-Virus-Scanned: amavisd-new at ICSI.Berkeley.EDU
Received: from rock.ICSI.Berkeley.EDU ([]) by localhost (maihub.ICSI.Berkeley.EDU []) (amavisd-new, port 10024) with LMTP id fGhLU-zNZj7m; Wed, 12 Feb 2014 07:35:47 -0800 (PST)
Received: from gala.icir.org (gala.icir.org []) (Authenticated sender: nweaver) by rock.ICSI.Berkeley.EDU (Postfix) with ESMTP id 721D12C4009; Wed, 12 Feb 2014 07:35:47 -0800 (PST)
From: Nicholas Weaver <nweaver@icsi.berkeley.edu>
Content-Type: multipart/signed; boundary="Apple-Mail=_6E3D2411-F1A7-42AF-859A-5E585CAD0C2A"; protocol="application/pgp-signature"; micalg=pgp-sha512
Date: Wed, 12 Feb 2014 07:35:47 -0800
Message-Id: <390BFAA1-3108-4922-A6C0-E666C0BD25A9@icsi.berkeley.edu>
To: dnsop WG <dnsop@ietf.org>
Mime-Version: 1.0 (Mac OS X Mail 7.1 \(1827\))
X-Mailer: Apple Mail (2.1827)
Cc: Nicholas Weaver <nweaver@icsi.berkeley.edu>
Subject: [DNSOP] Any suggestion on what I'm doing that is stupid here on NSEC3?
X-BeenThere: dnsop@ietf.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: IETF DNSOP WG mailing list <dnsop.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/dnsop>, <mailto:dnsop-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/dnsop/>
List-Post: <mailto:dnsop@ietf.org>
List-Help: <mailto:dnsop-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/dnsop>, <mailto:dnsop-request@ietf.org?subject=subscribe>
X-List-Received-Date: Wed, 12 Feb 2014 15:35:51 -0000

I'm trying to do my own implementation of NSEC3 as part of my dynamic DNSSEC server (in order to do NSEC3 lies for NXDOMAIN, since you can't do such a lie with NSEC, NSEC lies only allow "0 answer noerror" which is unfortunately NOT the same)

But I appear to be doing something stupid, and am not operating the hash right:

Looking at com, the NSEC3 for "com" is:
CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - ...

(Algorithm 1 -> SHA-1, flag = 1, iterations = 0, salt = None, fetched by "dig +dnssec MX com @a.gtld-servers.net")

Reading RFC5155, the calculation of the hash is:

>    The hash calculation uses three of the NSEC3 RDATA fields: Hash
>    Algorithm, Salt, and Iterations.
>    Define H(x) to be the hash of x using the Hash Algorithm selected by
>    the NSEC3 RR, k to be the number of Iterations, and || to indicate
>    concatenation.  Then define:
>       IH(salt, x, 0) = H(x || salt), and
>       IH(salt, x, k) = H(IH(salt, x, k-1) || salt), if k > 0
>    Then the calculated hash of an owner name is
>       IH(salt, owner name, iterations),
>    where the owner name is in the canonical form, defined as:
>    The wire format of the owner name where:
>    1.  The owner name is fully expanded (no DNS name compression) and
>        fully qualified;
>    2.  All uppercase US-ASCII letters are replaced by the corresponding
>        lowercase US-ASCII letters;
>    3.  If the owner name is a wildcard name, the owner name is in its
>        original unexpanded form, including the "*" label (no wildcard
>        substitution);

So it should be the base32 encoding of the SHA1 hash of the wire format for "com" (since there is no salt), which in python is:

"\x03com\x00", (3 characters, the string "com", and 0 as a terminator in wire format.  This matches the wire format I get from my name packer in my DNS server)

Yet when I try to calculate the SHA1 hash in python's library, I get:
>>> m = hashlib.sha1() 
>>> m.update("\x03com\x00") # There is no salt and 0 additional iterations
>>> base64.b32encode(m.digest()) 
>>> m.hexdigest()

But at the same time, this matches the sha1sum for a file containing just the string "\x03com\x00", so the hash is correct for sha1.

So the conclusion is I'm not putting in the right input into the hash function.  Thoughts on what I'm doing wrong?

Nicholas Weaver                  it is a tale, told by an idiot,
nweaver@icsi.berkeley.edu                full of sound and fury,
510-666-2903                                 .signifying nothing
PGP: http://www1.icsi.berkeley.edu/~nweaver/data/nweaver_pub.asc