From patchwork Mon Aug 4 12:48:18 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnaud Charlet X-Patchwork-Id: 376268 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 7C000140077 for ; Mon, 4 Aug 2014 22:48:34 +1000 (EST) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; q=dns; s=default; b=NylinbOWBGwy8hCavOy41Jb/DoWyqShfSFPOmUK43mc/q8i6O+ keOC2kreF1N5hAdCcKmeGwnS/P2tVuMdKEI8wQtTeSiBGyWhtIXUOHyYMLnm9OEe L2xFpY0t1sp8FFqUP8VIAe1Cy/j1xRMryp3mjGKEMOtl85USVeMSyoJ8w= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:date :from:to:cc:subject:message-id:mime-version:content-type; s= default; bh=37WA/Dy43H+CO8c+XFkisuv2FFk=; b=EmyYsPQyKpkvdknPke+h 0/+snJ7xZ9oec28X672LrRw1OHPmpMMGRSN34BlIp0wbbe141NfIfYEOcqfqkmT3 r4ZZlq9MB1vOGkrjpkQYO3WQNys2z56/fGwzf6Epllf9BrwFRhS0SapBBIdOJi0A ZLOUo6E+srhtgRqwtUfiUQA= Received: (qmail 31311 invoked by alias); 4 Aug 2014 12:48:27 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 31255 invoked by uid 89); 4 Aug 2014 12:48:22 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham version=3.3.2 X-HELO: rock.gnat.com Received: from rock.gnat.com (HELO rock.gnat.com) (205.232.38.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-SHA encrypted) ESMTPS; Mon, 04 Aug 2014 12:48:20 +0000 Received: from localhost (localhost.localdomain [127.0.0.1]) by filtered-rock.gnat.com (Postfix) with ESMTP id 7F95D116436; Mon, 4 Aug 2014 08:48:18 -0400 (EDT) Received: from rock.gnat.com ([127.0.0.1]) by localhost (rock.gnat.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id BVN36b6UN5bQ; Mon, 4 Aug 2014 08:48:18 -0400 (EDT) Received: from kwai.gnat.com (kwai.gnat.com [IPv6:2620:20:4000:0:a6ba:dbff:fe26:1f63]) by rock.gnat.com (Postfix) with ESMTP id 6D48A116425; Mon, 4 Aug 2014 08:48:18 -0400 (EDT) Received: by kwai.gnat.com (Postfix, from userid 4192) id 68EBA3FE21; Mon, 4 Aug 2014 08:48:18 -0400 (EDT) Date: Mon, 4 Aug 2014 08:48:18 -0400 From: Arnaud Charlet To: gcc-patches@gcc.gnu.org Cc: Thomas Quinot Subject: [Ada] Support for hash based message authentication codes Message-ID: <20140804124818.GA4077@adacore.com> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.21 (2010-09-15) This change introduces a new subprogram in the GNAT secure hash framework to allow computing HMACs based on the secure hash functions. This is achieved by initializing a context with a (non-empty) key. The following example shows how to compute the first HMAC-MD5 test from RFC2104: $ gnatmake -q rfc2104_test1.adb $ ./rfc2104_test1 9294727a3638bb1c13f48ef8158bfc9d with Ada.Text_IO; use Ada.Text_IO; with GNAT.MD5; use GNAT.MD5; procedure RFC2104_Test1 is C : Context := HMAC_Initial_Context ((1 .. 16 => Character'Val (16#0b#))); begin Update (C, "Hi There"); Put_Line (Digest (C)); end RFC2104_Test1; Tested on x86_64-pc-linux-gnu, committed on trunk 2014-08-04 Thomas Quinot * g-sechas.ads, g-sechas.adb (HMAC_Initial_Context): New subprogram. * gnat_rm.texi (GNAT.MD5/SHA1/SHA224/SHA256/SHA512): Document support for HMAC. Index: gnat_rm.texi =================================================================== --- gnat_rm.texi (revision 213566) +++ gnat_rm.texi (working copy) @@ -19952,7 +19952,9 @@ @cindex Message Digest MD5 @noindent -Implements the MD5 Message-Digest Algorithm as described in RFC 1321. +Implements the MD5 Message-Digest Algorithm as described in RFC 1321, and +the HMAC-MD5 message authentication function as described in RFC 2104 and +FIPS PUB 198. @node GNAT.Memory_Dump (g-memdum.ads) @section @code{GNAT.Memory_Dump} (@file{g-memdum.ads}) @@ -20088,7 +20090,8 @@ @noindent Implements the SHA-1 Secure Hash Algorithm as described in FIPS PUB 180-3 -and RFC 3174. +and RFC 3174, and the HMAC-SHA1 message authentication function as described +in RFC 2104 and FIPS PUB 198. @node GNAT.SHA224 (g-sha224.ads) @section @code{GNAT.SHA224} (@file{g-sha224.ads}) @@ -20096,7 +20099,9 @@ @cindex Secure Hash Algorithm SHA-224 @noindent -Implements the SHA-224 Secure Hash Algorithm as described in FIPS PUB 180-3. +Implements the SHA-224 Secure Hash Algorithm as described in FIPS PUB 180-3, +and the HMAC-SHA224 message authentication function as described +in RFC 2104 and FIPS PUB 198. @node GNAT.SHA256 (g-sha256.ads) @section @code{GNAT.SHA256} (@file{g-sha256.ads}) @@ -20104,7 +20109,9 @@ @cindex Secure Hash Algorithm SHA-256 @noindent -Implements the SHA-256 Secure Hash Algorithm as described in FIPS PUB 180-3. +Implements the SHA-256 Secure Hash Algorithm as described in FIPS PUB 180-3, +and the HMAC-SHA256 message authentication function as described +in RFC 2104 and FIPS PUB 198. @node GNAT.SHA384 (g-sha384.ads) @section @code{GNAT.SHA384} (@file{g-sha384.ads}) @@ -20112,7 +20119,9 @@ @cindex Secure Hash Algorithm SHA-384 @noindent -Implements the SHA-384 Secure Hash Algorithm as described in FIPS PUB 180-3. +Implements the SHA-384 Secure Hash Algorithm as described in FIPS PUB 180-3, +and the HMAC-SHA384 message authentication function as described +in RFC 2104 and FIPS PUB 198. @node GNAT.SHA512 (g-sha512.ads) @section @code{GNAT.SHA512} (@file{g-sha512.ads}) @@ -20120,7 +20129,9 @@ @cindex Secure Hash Algorithm SHA-512 @noindent -Implements the SHA-512 Secure Hash Algorithm as described in FIPS PUB 180-3. +Implements the SHA-512 Secure Hash Algorithm as described in FIPS PUB 180-3, +and the HMAC-SHA512 message authentication function as described +in RFC 2104 and FIPS PUB 198. @node GNAT.Signals (g-signal.ads) @section @code{GNAT.Signals} (@file{g-signal.ads}) Index: g-sechas.adb =================================================================== --- g-sechas.adb (revision 213536) +++ g-sechas.adb (working copy) @@ -6,7 +6,7 @@ -- -- -- B o d y -- -- -- --- Copyright (C) 2009-2012, Free Software Foundation, Inc. -- +-- Copyright (C) 2009-2014, Free Software Foundation, Inc. -- -- -- -- GNAT is free software; you can redistribute it and/or modify it under -- -- terms of the GNU General Public License as published by the Free Soft- -- @@ -218,8 +218,8 @@ -- the message size in bits (excluding padding). procedure Final - (C : Context; - Hash_Bits : out Stream_Element_Array) + (C : Context; + Hash_Bits : out Stream_Element_Array) is FC : Context := C; @@ -274,8 +274,73 @@ pragma Assert (FC.M_State.Last = 0); Hash_State.To_Hash (FC.H_State, Hash_Bits); + + -- HMAC case: hash outer pad + + if C.KL /= 0 then + declare + Outer_C : Context; + Opad : Stream_Element_Array := + (1 .. Stream_Element_Offset (Block_Length) => 16#5c#); + + begin + for J in C.Key'Range loop + Opad (J) := Opad (J) xor C.Key (J); + end loop; + + Update (Outer_C, Opad); + Update (Outer_C, Hash_Bits); + + Final (Outer_C, Hash_Bits); + end; + end if; end Final; + -------------------------- + -- HMAC_Initial_Context -- + -------------------------- + + function HMAC_Initial_Context (Key : String) return Context is + begin + if Key'Length = 0 then + raise Constraint_Error with "null key"; + end if; + + return C : Context (KL => (if Key'Length <= Key_Length'Last + then Key'Length + else Stream_Element_Offset (Hash_Length))) + do + -- Set Key (if longer than block length, first hash it) + + if C.KL = Key'Length then + declare + SK : String (1 .. Key'Length); + for SK'Address use C.Key'Address; + pragma Import (Ada, SK); + begin + SK := Key; + end; + + else + C.Key := Digest (Key); + end if; + + -- Hash inner pad + + declare + Ipad : Stream_Element_Array := + (1 .. Stream_Element_Offset (Block_Length) => 16#36#); + + begin + for J in C.Key'Range loop + Ipad (J) := Ipad (J) xor C.Key (J); + end loop; + + Update (C, Ipad); + end; + end return; + end HMAC_Initial_Context; + ------------ -- Update -- ------------ @@ -285,11 +350,12 @@ S : String; Fill_Buffer : Fill_Buffer_Access) is - Last : Natural := S'First - 1; + Last : Natural; begin C.M_State.Length := C.M_State.Length + S'Length; + Last := S'First - 1; while Last < S'Last loop Fill_Buffer (C.M_State, S, Last + 1, Last); Index: g-sechas.ads =================================================================== --- g-sechas.ads (revision 213536) +++ g-sechas.ads (working copy) @@ -6,7 +6,7 @@ -- -- -- S p e c -- -- -- --- Copyright (C) 2009-2012, Free Software Foundation, Inc. -- +-- Copyright (C) 2009-2014, Free Software Foundation, Inc. -- -- -- -- GNAT is free software; you can redistribute it and/or modify it under -- -- terms of the GNU General Public License as published by the Free Soft- -- @@ -144,6 +144,9 @@ -- Initial value of a Context object. May be used to reinitialize -- a Context value by simple assignment of this value to the object. + function HMAC_Initial_Context (Key : String) return Context; + -- Initial Context for HMAC computation with the given Key + procedure Update (C : in out Context; Input : String); procedure Wide_Update (C : in out Context; Input : Wide_String); procedure Update @@ -163,7 +166,7 @@ -- the hash in binary representation. function Digest (C : Context) return Binary_Message_Digest; - -- Return hash for the data accumulated with C + -- Return hash or HMAC for the data accumulated with C function Digest (S : String) return Binary_Message_Digest; function Wide_Digest (W : Wide_String) return Binary_Message_Digest; @@ -178,7 +181,7 @@ -- hexadecimal representation. function Digest (C : Context) return Message_Digest; - -- Return hash for the data accumulated with C in hexadecimal + -- Return hash or HMAC for the data accumulated with C in hexadecimal -- representation. function Digest (S : String) return Message_Digest; @@ -193,7 +196,15 @@ Block_Length : constant Natural := Block_Words * Word_Length; -- Length in bytes of a data block - type Context is record + subtype Key_Length is + Stream_Element_Offset range 0 .. Stream_Element_Offset (Block_Length); + + -- KL is 0 for a normal hash context, > 0 for HMAC + + type Context (KL : Key_Length := 0) is record + Key : Stream_Element_Array (1 .. KL); + -- HMAC key + H_State : Hash_State.State (0 .. State_Words - 1) := Initial_State; -- Function-specific state @@ -201,7 +212,7 @@ -- Function-independent state (block buffer) end record; - Initial_Context : constant Context := (others => <>); + Initial_Context : constant Context (KL => 0) := (others => <>); -- Initial values are provided by default initialization of Context end H;