{******************************************************************************} { } { Library: Fundamentals 5.00 } { File name: flcCipherDH.pas } { File version: 5.08 } { Description: Diffie-Hellman (DH) cipher routines } { } { Copyright: Copyright (c) 2010-2020, David J Butler } { All rights reserved. } { This file is licensed under the BSD License. } { See http://www.opensource.org/licenses/bsd-license.php } { Redistribution and use in source and binary forms, with } { or without modification, are permitted provided that } { the following conditions are met: } { Redistributions of source code must retain the above } { copyright notice, this list of conditions and the } { following disclaimer. } { THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND } { CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED } { WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED } { WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A } { PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL } { THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, } { INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR } { CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, } { PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF } { USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) } { HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER } { IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING } { NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE } { USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE } { POSSIBILITY OF SUCH DAMAGE. } { } { Github: https://github.com/fundamentalslib } { E-mail: fundamentals.library at gmail.com } { } { References: } { } { RFC 2631 - Diffie-Hellman Key Agreement Method } { RFC 5114 - Additional Diffie-Hellman Groups } { RFC 3526 - MODP Diffie-Hellman groups for IKE } { RFC 2409 - The Internet Key Exchange (IKE) } { http://www.faqs.org/rfcs/rfc2539.html } { https://weakdh.org/ } { http://www.floatingdoghead.net/bigprimes.html } { https://www.cryptopp.com/wiki/Diffie-Hellman } { http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf } { } { Revision history: } { } { 2010/11/07 0.01 Initial development (Primes, OtherInfo) } { 2010/11/10 0.02 Further development (KEK) } { 2010/12/03 0.03 Changes to prime generation based on RFC errata. } { 2016/01/07 0.04 Improvements and tests (KM, KEK and generation). } { 2016/01/08 0.05 Secure random. } { 2016/01/09 5.06 Revised for Fundamentals 5. } { 2016/01/29 5.07 Well known groups, tests and fixes. } { 2018/07/17 5.08 Types changes. } { } { Todo: } { - SHA256 Hash algorithm support } { - Error codes } { - Split up TDHState } { - Callbacks for slow operations } {******************************************************************************} {$INCLUDE flcCipher.inc} unit flcCipherDH; interface uses { System } SysUtils, { Fundamentals } flcStdTypes, flcHugeInt; type EDH = class(Exception); TDHHashAlgorithm = ( dhhSHA1, dhhSHA256); TDHState = record HashAlgorithm : TDHHashAlgorithm; HashBitCount : Integer; PrimeQBitCount : Integer; // Bits in Q = m PrimePBitCount : Integer; // Bits in P = L P : HugeWord; // Group param: Prime P Q : HugeWord; // Group param: Prime Q Seed : HugeWord; // Group param: Seed Counter : Integer; // Group param: P counter J : HugeWord; // Group param: Generator validation parameter G : HugeWord; // Generator X : HugeWord; // Private key (Xa) Y : HugeWord; // Public key (Ya) ZZ : HugeWord; // Shared secret RemoteKeySize : Integer; // Remote public key (Yb) RemoteKey : HugeWord; // Remote public key (Yb) KEK : RawByteString; end; PDHState = ^TDHState; procedure DHStateInit(var State: TDHState); procedure DHStateFinalise(var State: TDHState); procedure DHInitHashAlgorithm(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm); procedure DHGeneratePrimeQ(var State: TDHState; const Bits: Integer; const FixedSeed: Boolean); procedure DHGeneratePrimeP(var State: TDHState; const BitsP: Integer); procedure DHGeneratePrimesPQ( var State: TDHState; const PrimeQBitCount, PrimePBitCount: Integer; const Validating: Boolean); procedure DHGenerateG(var State: TDHState); function DHIsGeneratedGroupParameterValid(const State: TDHState): Boolean; procedure DHGeneratePrivateKeyX(var State: TDHState); procedure DHGeneratePublicKeyY(var State: TDHState); function DHQBitCount(const PBitCount: Integer): Integer; procedure DHGenerateKeys(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer); procedure DHDeriveKeysFromGroupParametersPGQ(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer; const P, G, Q: HugeWord); procedure DHDeriveKeysFromGroupParametersPG(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer; const P, G: HugeWord); procedure DHDeriveKeysFromGroupParameterP1(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer; const P: HugeWord); procedure DHDeriveKeysFromGroupParameterP2(var State: TDHState); function DHIsPublicKeyValid(const State: TDHState; const Y: HugeWord): Boolean; function DHHugeWordKeyEncodeBytes(const A: HugeWord): RawByteString; procedure DHHugeWordKeyDecodeBytes(var A: HugeWord; const B: RawByteString); procedure DHGenerateSharedSecretZZ( var State: TDHState; const RemotePublicKeySize: Integer; const RemotePublicKey: HugeWord); procedure DHGenerateKEK( var State: TDHState; const CipherOID: array of Integer; const CipherKeyBits: Integer; const PartyAInfo: RawByteString); type TDHWellKnownGroup = record Description : String; Source : String; IKEGroupId : Integer; PBitCount : Integer; QBitCount : Integer; P_Hex : RawByteString; G_Hex : RawByteString; end; PDHWellKnownGroup = ^TDHWellKnownGroup; const DHWellKnownGroups = 12; DHWellKnownGroup: array[0..DHWellKnownGroups - 1] of TDHWellKnownGroup = ( ( Description: 'RFC 2409 - 6.1 - First Oakley Default Group - 2^768 - 2 ^704 - 1 + 2^64 * ( [2^638 pi] + 149686 )'; IKEGroupId: 1; PBitCount: 768; QBitCount: 160; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 2409 - 6.2 - Second Oakley Group - 2^1024 - 2^960 - 1 + 2^64 * ( [2^894 pi] + 129093 )'; IKEGroupId: 2; PBitCount: 1024; QBitCount: 160; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381' + 'FFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 3526 - 2 - 1536-bit MODP Group - 2^1536 - 2^1472 - 1 + 2^64 * ( [2^1406 pi] + 741804 )'; IKEGroupId: 5; PBitCount: 1536; QBitCount: 160; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + '670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 3526 - 3 - 2048-bit MODP Group - 2^2048 - 2^1984 - 1 + 2^64 * ( [2^1918 pi] + 124476 )'; IKEGroupId: 14; PBitCount: 2048; QBitCount: 256; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9' + 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510' + '15728E5A8AACAA68FFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 3526 - 4 - 3072-bit MODP Group - 2^3072 - 2^3008 - 1 + 2^64 * ( [2^2942 pi] + 1690314 )'; IKEGroupId: 15; PBitCount: 3072; QBitCount: 256; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9' + 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510' + '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64' + 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' + 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B' + 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C' + 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31' + '43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 3526 - 5 - 4096-bit MODP Group - 2^4096 - 2^4032 - 1 + 2^64 * ( [2^3966 pi] + 240904 )'; IKEGroupId: 16; PBitCount: 4096; QBitCount: 256; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9' + 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510' + '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64' + 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' + 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B' + 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C' + 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31' + '43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' + '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA' + '2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6' + '287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED' + '1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' + '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199' + 'FFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 3526 - 6 - 6144-bit MODP Group - 2^6144 - 2^6080 - 1 + 2^64 * ( [2^6014 pi] + 929484 )'; IKEGroupId: 17; PBitCount: 6144; QBitCount: 256; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08' + '8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B' + '302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9' + 'A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6' + '49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8' + 'FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D' + '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C' + '180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' + '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D' + '04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D' + 'B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226' + '1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C' + 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC' + 'E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26' + '99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB' + '04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' + '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127' + 'D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492' + '36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406' + 'AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918' + 'DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151' + '2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03' + 'F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F' + 'BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' + 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B' + 'B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632' + '387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E' + '6DCC4024FFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 3526 - 7 - 8192-bit MODP Group - 2^8192 - 2^8128 - 1 + 2^64 * ( [2^8062 pi] + 4743158 )'; IKEGroupId: 18; PBitCount: 8192; QBitCount: 256; P_Hex: 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + 'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + 'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + 'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + 'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' + 'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9' + 'DE2BCBF6955817183995497CEA956AE515D2261898FA0510' + '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64' + 'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' + 'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B' + 'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C' + 'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31' + '43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' + '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA' + '2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6' + '287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED' + '1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' + '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492' + '36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD' + 'F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831' + '179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' + 'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF' + '5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6' + 'D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3' + '23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' + 'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328' + '06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C' + 'DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE' + '12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4' + '38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300' + '741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568' + '3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9' + '22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B' + '4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A' + '062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36' + '4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1' + 'B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92' + '4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47' + '9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71' + '60C980DD98EDD3DFFFFFFFFFFFFFFFFF'; G_Hex: '00000002' ), ( Description: 'RFC 5114 - 2.1 - 1024-bit MODP Group with 160-bit Prime Order Subgroup'; IKEGroupId: 22; PBitCount: 1024; QBitCount: 160; P_Hex: 'B10B8F96A080E01DDE92DE5EAE5D54EC52C99FBCFB06A3C6' + '9A6A9DCA52D23B616073E28675A23D189838EF1E2EE652C0' + '13ECB4AEA906112324975C3CD49B83BFACCBDD7D90C4BD70' + '98488E9C219A73724EFFD6FAE5644738FAA31A4FF55BCCC0' + 'A151AF5F0DC8B4BD45BF37DF365C1A65E68CFDA76D4DA708' + 'DF1FB2BC2E4A4371'; G_Hex: 'A4D1CBD5C3FD34126765A442EFB99905F8104DD258AC507F' + 'D6406CFF14266D31266FEA1E5C41564B777E690F5504F213' + '160217B4B01B886A5E91547F9E2749F4D7FBD7D3B9A92EE1' + '909D0D2263F80A76A6A24C087A091F531DBF0A0169B6A28A' + 'D662A4D18E73AFA32D779D5918D08BC8858F4DCEF97C2A24' + '855E6EEB22B3B2E5' ), ( Description: 'RFC 5114 - 2.2 - 2048-bit MODP Group with 224-bit Prime Order Subgroup'; IKEGroupId: 23; PBitCount: 2048; QBitCount: 224; P_Hex: 'AD107E1E9123A9D0D660FAA79559C51FA20D64E5683B9FD1' + 'B54B1597B61D0A75E6FA141DF95A56DBAF9A3C407BA1DF15' + 'EB3D688A309C180E1DE6B85A1274A0A66D3F8152AD6AC212' + '9037C9EDEFDA4DF8D91E8FEF55B7394B7AD5B7D0B6C12207' + 'C9F98D11ED34DBF6C6BA0B2C8BBC27BE6A00E0A0B9C49708' + 'B3BF8A317091883681286130BC8985DB1602E714415D9330' + '278273C7DE31EFDC7310F7121FD5A07415987D9ADC0A486D' + 'CDF93ACC44328387315D75E198C641A480CD86A1B9E587E8' + 'BE60E69CC928B2B9C52172E413042E9B23F10B0E16E79763' + 'C9B53DCF4BA80A29E3FB73C16B8E75B97EF363E2FFA31F71' + 'CF9DE5384E71B81C0AC4DFFE0C10E64F'; G_Hex: 'AC4032EF4F2D9AE39DF30B5C8FFDAC506CDEBE7B89998CAF' + '74866A08CFE4FFE3A6824A4E10B9A6F0DD921F01A70C4AFA' + 'AB739D7700C29F52C57DB17C620A8652BE5E9001A8D66AD7' + 'C17669101999024AF4D027275AC1348BB8A762D0521BC98A' + 'E247150422EA1ED409939D54DA7460CDB5F6C6B250717CBE' + 'F180EB34118E98D119529A45D6F834566E3025E316A330EF' + 'BB77A86F0C1AB15B051AE3D428C8F8ACB70A8137150B8EEB' + '10E183EDD19963DDD9E263E4770589EF6AA21E7F5F2FF381' + 'B539CCE3409D13CD566AFBB48D6C019181E1BCFE94B30269' + 'EDFE72FE9B6AA4BD7B5A0F1C71CFFF4C19C418E1F6EC0179' + '81BC087F2A7065B384B890D3191F2BFA'; ), ( Description: 'RFC 5114 - 2.3 - 2048-bit MODP Group with 256-bit Prime Order Subgroup'; IKEGroupId: 24; PBitCount: 2048; QBitCount: 256; P_Hex: '87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F2' + '5D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA30' + '16C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD' + '5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B' + '6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C' + '4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0E' + 'F13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D9' + '67E144E5140564251CCACB83E6B486F6B3CA3F7971506026' + 'C0B857F689962856DED4010ABD0BE621C3A3960A54E710C3' + '75F26375D7014103A4B54330C198AF126116D2276E11715F' + '693877FAD7EF09CADB094AE91E1A1597'; G_Hex: '3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF2054' + '07F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555' + 'BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18' + 'A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B' + '777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC83' + '1D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55' + 'A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14' + 'C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915' + 'B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6' + '184B523D1DB246C32F63078490F00EF8D647D148D4795451' + '5E2327CFEF98C582664B4C0F6CC41659' ), ( Description: 'http://www.floatingdoghead.net/bigprimes.html - 8192-bit secure Diffie-Hellman modulus #1'; IKEGroupId: -1; PBitCount: 8192; QBitCount: 192; P_Hex: '99134f625a7e28b61a2e610e1d55c5b1c01dc37f718c16116e482f500a046cca' + '650df4bea06121e842c16e3208548c51dbfe07a34fba510d61c5961d8920c41b' + '516e47ead343d0152f71ff692ccfd078b90323f0405ab12a0f5b1717236242b5' + 'b4382b65bfeee7a8ed102a9fd8870b42a6b54a7667cfb0ac6a58de6b7ef1d7ca' + 'db892d859ec9cd5feef5ca54127aa75aa84d20ea88d3085d3fab4ea71a82b4c0' + 'ad0b9139d68d9521ca9ca4bef9f2221f96c7b3d93564e7735ea1514e318baa24' + '72e46a0f474fcfcafab662843d172ab0a5021e27fd8dd3c077f876350029679a' + '25de16eb0df0371d889f48d709ae68638dc2c82e797dccec72f8dec9842ba8a7' + 'a9b9a047afc98769252b1c3db5805b7e7a91b4860eb785cd0eaa575f9313a876' + '2eca9929aed02f5fbd660a11c2a3bb70e2836a471928d283ab40fc470cf71331' + 'eb51e6a09ad94b8f50f33af6c8af995fc943616467a6815e5d32cfb25a084ac6' + '386746dd5ab339283e643bf3ef7d488838769b2990776b728b1de7e605c8294f' + 'cc7ecd9092a357d3286f7827bf921e4fb7239db2f5aa415ec53be0a5b26f7063' + '2191929b7dc9a8e17917b308884bb0745e8420fc6639a570ef45e3eae39a9444' + 'cdcfc4509775d32f297a47025fd706fd6854a6a327981c4e4e56280ac590e63b' + 'cd6dea98682a4c655de964ae0de75922b0f88a071950029de9f6ce673599ebb7' + '4bb122969b7c065a10ee2e6e60c41e8acae2ea6da0097810192d17198756dd22' + 'bc1b533602e60dab38c0146b02394c7ba727da464bd7ab5bbd0b3c287522caf5' + '1f69db508fc5c603a7832bfbdba41f7467e3a565984368421e489cf04a44a0d4' + 'fb5bd58237773506284131ea99c0c55545729d1b56473e2ee83e73c74fd19b5f' + '076115a85042370028e899c6cdaac1be1e9668518be1e48aecf40de573da2241' + '3e20a2d64d6ff935c5899f1c3c40eec8c33f9389aaff8ea6cab749199e09084f' + '94aa7dfe5257c09c601198ae84225cf1f995ce8610a1c21f79d767f177c75f6b' + '3a5575e3880b021e67f9c8bca52a6b8ce4ceec522fae129a08a23288f570403e' + '8acd8973b490fae5a6e669a5291afee18550b3d6e96100ee41b32b92e48056ad' + '626451d132c4676496776951eff8c672a480d297fb9d12aaa3faee456ea93953' + 'b1ca94ceeaeb6e4091fd820df5d6bfc2fed4a2587a096f8b5267600a97c4b9eb' + 'bfa91b5579dbe1cfaf7c6018bc4a1d679204c2e729c492eba06bd506f77b2403' + 'd57730d1c73dcc861aabb2f3e90ffdb9a12bf3298eb34924c2be999bbcc6e4d7' + 'dea62e98a1ea40943c998142a9947a13eef3e1531506a30b369b399bf22868cf' + 'c79684e07e8dad0a936d8db81e876f3711dc1ea0c53fc19e234f00f826ccc2f8' + '8aa0e2ae60eebaff8a98977924c2054429454ca000c5c2aa22fccfc5e2fdd553'; G_Hex: '' ) ); { } { Test cases } { } {$IFDEF CIPHER_TEST} procedure Test; {$ENDIF} implementation uses { Fundamentals } flcHash, flcRandom, flcASN1, flcCipherRandom; { } { SecureClear } { } procedure SecureClearHugeWord(var A: HugeWord); begin if (A.Alloc = 0) or not Assigned(A.Data) then exit; SecureClear(A.Data^, A.Alloc * HugeWordElementSize); end; procedure SecureHugeWordFinalise(var A: HugeWord); begin SecureClearHugeWord(A); HugeWordFinalise(A); end; { } { SecureHugeWordRandom } { } procedure SecureHugeWordRandom(var A: HugeWord; const Size: Integer); begin HugeWordSetSize(A, Size); if Size <= 0 then exit; SecureRandomBuf(A.Data^, Size * HugeWordElementSize); end; { } { DH } { } const SPrimeGenerationFailure = 'Failed to generate prime'; SInvalidParameter = 'Invalid parameter'; procedure DHStateInit(var State: TDHState); begin FillChar(State, SizeOf(TDHState), 0); HugeWordInit(State.P); HugeWordInit(State.Q); HugeWordInit(State.Seed); HugeWordInit(State.J); HugeWordInit(State.G); HugeWordInit(State.X); HugeWordInit(State.Y); HugeWordInit(State.ZZ); HugeWordInit(State.RemoteKey); end; procedure DHStateFinalise(var State: TDHState); begin SecureHugeWordFinalise(State.RemoteKey); SecureHugeWordFinalise(State.ZZ); SecureHugeWordFinalise(State.Y); SecureHugeWordFinalise(State.X); SecureHugeWordFinalise(State.G); SecureHugeWordFinalise(State.J); SecureHugeWordFinalise(State.Seed); SecureHugeWordFinalise(State.Q); SecureHugeWordFinalise(State.P); end; const HashAlgorithmBitCount : array[TDHHashAlgorithm] of Integer = (160, 256); procedure DHInitHashAlgorithm(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm); begin State.HashAlgorithm := HashAlgorithm; State.HashBitCount := HashAlgorithmBitCount[HashAlgorithm]; end; procedure DHHugeWordSHA1(const A: HugeWord; var B: HugeWord); var H : T160BitDigest; begin H := CalcSHA1(A.Data^, A.Used * HugeWordElementSize); HugeWordSetSize(B, SHA1DigestBits div HugeWordElementBits); Move(H.Word32s[0], B.Data^, SHA1DigestSize); end; procedure DHRandomSeed(const Bits: Integer; var Seed: HugeWord); var N, I : Integer; begin Assert(Bits >= HugeWordElementBits); N := (Bits + HugeWordElementBits - 1) div HugeWordElementBits; SecureHugeWordRandom(Seed, N); for I := Bits to N * HugeWordElementBits - 1 do HugeWordClearBit(Seed, I); end; procedure DHGeneratePrimeQ_CalcU(var U: HugeWord; const State: TDHState; const M, MP, HashBitCount: Integer); var I, F : Integer; T1, T2 : HugeWord; begin Assert(MP > 0); Assert(HashBitCount > 0); Assert(not HugeWordIsZero(State.Seed)); Assert(State.HashAlgorithm = dhhSHA1); Assert(State.HashBitCount = 160); Assert(HashBitCount = State.HashBitCount); HugeWordInit(T1); HugeWordInit(T2); try // set U = 0 HugeWordAssignZero(U); // for i = 0 to m' - 1 for I := 0 to MP - 1 do begin // http://www.rfc-editor.org/errata_search.php?rfc=2631 // Modified version from errata for RFC 2631: // U = U + ( // SHA1[SEED + i] XOR // SHA1[(SEED + m' + i) mod (2^seedlen)] // ) // * 2^(160 * i) // // T1 = SHA1[SEED + i] HugeWordAssign(T1, State.Seed); HugeWordAddWord32(T1, I); DHHugeWordSHA1(T1, T1); // T2 = SEED + m' + i HugeWordAssign(T2, State.Seed); HugeWordAddWord32(T2, MP + I); // T2 = T2 mod (2^seedlen) for F := M to HugeWordGetBitCount(T2) - 1 do HugeWordClearBit(T2, F); HugeWordNormalise(T2); // T2 = SHA1[T2] DHHugeWordSHA1(T2, T2); // T1 = T1 XOR T2 HugeWordXorHugeWord(T1, T2); // T1 = T1 * 2^(160 * i) HugeWordShl(T1, HashBitCount * I); // U = U + T1 HugeWordAdd(U, T1); end; finally SecureHugeWordFinalise(T2); SecureHugeWordFinalise(T1); end; end; procedure DHGeneratePrimeQ(var State: TDHState; const Bits: Integer; const FixedSeed: Boolean); var M, MP, H : Integer; Q, U : HugeWord; R : Boolean; I : Integer; begin Assert(Bits > 0); Assert(State.HashBitCount > 0); HugeWordInit(Q); HugeWordInit(U); try M := Bits; H := State.HashBitCount; // set m' = m / 160 where / represents integer division with rounding upwards i.e. 200/160 = 2 MP := (M + H - 1) div H; repeat // select an arbitrary bit string SEED such that the length of SEED >= m if not FixedSeed then DHRandomSeed(M, State.Seed); // calculate U DHGeneratePrimeQ_CalcU(U, State, M, MP, H); // form q from U by computing U mod (2^m) and setting the most significant // bit (the 2^(m-1) bit) and the least significant bit to 1. In terms of // boolean operations, q = U OR 2^(m-1) OR 1. Note that 2^(m-1) < q < 2^m HugeWordAssign(Q, U); for I := M to HugeWordGetSizeInBits(Q) - 1 do HugeWordClearBit(Q, I); HugeWordSetBit(Q, 0); HugeWordSetBit(Q, M - 1); HugeWordNormalise(Q); // use a robust primality algorithm to test whether q is prime R := HugeWordIsPrime(Q) <> pNotPrime; if FixedSeed and not R then raise EDH.Create(SPrimeGenerationFailure); until R; State.PrimeQBitCount := M; HugeWordAssign(State.Q, Q); finally SecureHugeWordFinalise(U); SecureHugeWordFinalise(Q); end; end; procedure DHGeneratePrimeP(var State: TDHState; const BitsP: Integer); var L, M, LP, NP, MP, C, I, H, IZ : Integer; P, R, T1, T2, V, W, X : HugeWord; PR : Boolean; begin Assert(BitsP > 0); Assert(State.HashBitCount > 0); Assert(State.PrimeQBitCount > 0); Assert(not HugeWordIsZero(State.Q)); Assert(State.HashAlgorithm = dhhSHA1); Assert(State.HashBitCount = 160); HugeWordInit(P); HugeWordInit(R); HugeWordInit(T1); HugeWordInit(T2); HugeWordInit(V); HugeWordInit(W); HugeWordInit(X); try H := State.HashBitCount; // set L' = L/160, set N' = L/1024 L := BitsP; M := State.PrimeQBitCount; LP := (L + H - 1) div H; NP := (L + 1023) div 1024; MP := (M + H - 1) div H; // let counter = 0 C := 0; repeat // set R = Seed + 2*m' + (L' * counter) HugeWordAssign(R, State.Seed); HugeWordAddWord32(R, 2 * MP + LP * C); // set V = 0 HugeWordAssignZero(V); // for i = 0 to L' - 1 do for I := 0 to LP - 1 do begin // V = V + SHA1(R + i) * 2^(160 * i) HugeWordAssign(T1, R); HugeWordAddWord32(T1, I); DHHugeWordSHA1(T1, T1); if I > 0 then HugeWordShl(T1, H * I); HugeWordAdd(V, T1); end; // set W = V mod 2^L HugeWordAssign(W, V); for IZ := L to HugeWordGetBitCount(W) - 1 do HugeWordClearBit(W, IZ); // set X = W OR 2^(L-1) // note that 0 <= W < 2^(L-1) and hence X >= 2^(L-1) HugeWordAssign(X, W); HugeWordSetBit(X, L - 1); HugeWordNormalise(X); // set p = X - (X mod (2*q)) + 1 HugeWordAssign(P, X); // T1 = 2 * Q HugeWordAssign(T1, State.Q); HugeWordShl(T1, 1); // T2 = X mod (2*q) HugeWordMod(X, T1, T2); HugeWordSubtract(P, T2); HugeWordAddWord32(P, 1); // if p > 2^(L-1) use a robust primality test to test whether p is prime if not HugeWordIsBitSet(P, L - 1) then PR := False else PR := HugeWordIsPrime(P) <> pNotPrime; // if p is prime output p, q, seed, counter and stop, else // set counter = counter + 1 if not PR then Inc(C); // if counter < (4096 * N) then repeat until PR or (C >= 4096 * NP); if not PR then raise EDH.Create(SPrimeGenerationFailure); State.PrimePBitCount := L; State.Counter := C; HugeWordAssign(State.P, P); finally SecureHugeWordFinalise(X); SecureHugeWordFinalise(W); SecureHugeWordFinalise(V); SecureHugeWordFinalise(T2); SecureHugeWordFinalise(T1); SecureHugeWordFinalise(R); SecureHugeWordFinalise(P); end; end; procedure DHGeneratePrimesPQ( var State: TDHState; const PrimeQBitCount, PrimePBitCount: Integer; const Validating: Boolean); begin Assert(PrimeQBitCount >= 160); Assert(PrimePBitCount > PrimeQBitCount); DHGeneratePrimeQ(State, PrimeQBitCount, Validating); DHGeneratePrimeP(State, PrimePBitCount); end; { g = h^[(p-1)/q] mod p, where h is any integer with 1 < h < p-1 such that h[(p-1)/q] mod p > 1 (g has order q mod p; i.e. g^q mod p = 1 if g!=1) j a large integer such that p=qj + 1 } procedure DHGenerateG(var State: TDHState); var J, T1, T2, H, G : HugeWord; begin Assert(not HugeWordIsZero(State.P)); Assert(not HugeWordIsZero(State.Q)); HugeWordInit(J); HugeWordInit(T1); HugeWordInit(T2); HugeWordInit(H); HugeWordInit(G); try // 1. Let j = (p - 1)/q. // T1 = p - 1 HugeWordAssign(T1, State.P); HugeWordSubtractWord32(T1, 1); // J = T1 / q HugeWordDivide(T1, State.Q, J, T2); repeat // 2. Set h = any integer, where 1 < h < p - 1 and h differs from any value previously tried. repeat SecureHugeWordRandom(H, State.P.Used); until (HugeWordCompare(H, T1) < 0) and (HugeWordCompareWord32(H, 1) > 0); // 3. Set g = h^j mod p HugeWordPowerAndMod(G, H, J, State.P); // 4. If g = 1 go to step 2 until not HugeWordIsOne(G); HugeWordAssign(State.J, J); HugeWordAssign(State.G, G); finally SecureHugeWordFinalise(G); SecureHugeWordFinalise(H); SecureHugeWordFinalise(T2); SecureHugeWordFinalise(T1); SecureHugeWordFinalise(J); end; end; { 2.2.2. Group Parameter Validation The ASN.1 for DH keys in [PKIX] includes elements j and validation- Parms which MAY be used by recipients of a key to verify that the group parameters were correctly generated. Two checks are possible: 1. Verify that p=qj + 1. This demonstrates that the parameters meet the X9.42 parameter criteria. 2. Verify that when the p,q generation procedure of [FIPS-186] Appendix 2 is followed with seed 'seed', that p is found when 'counter' = pgenCounter. This demonstrates that the parameters were randomly chosen and do not have a special form. Whether agents provide validation information in their certificates is a local matter between the agents and their CA. } function DHIsGeneratedGroupParameterValid(const State: TDHState): Boolean; var T : HugeWord; D : TDHState; begin Assert(not HugeWordIsZero(State.P)); Assert(not HugeWordIsZero(State.Q)); Assert(not HugeWordIsZero(State.J)); Assert(not HugeWordIsZero(State.Seed)); HugeWordInit(T); try HugeWordMultiply(T, State.Q, State.J); HugeWordInc(T); // validation 1 if not HugeWordEquals(T, State.P) then Result := False else begin // validation 2 DHStateInit(D); try DHInitHashAlgorithm(D, State.HashAlgorithm); HugeWordAssign(D.Seed, State.Seed); DHGeneratePrimesPQ(D, State.PrimeQBitCount, State.PrimePBitCount, True); if (D.Counter <> State.Counter) then Result := False else if not HugeWordEquals(D.P, State.P) then Result := False else Result := True; finally DHStateFinalise(D); end; end; finally SecureHugeWordFinalise(T); end; end; { X9.42 requires that the private key x be in the interval [2, (q - 2)]. x should be randomly generated in this interval. y is then computed by calculating g^x mod p. To comply with this memo, m MUST be >=160 bits in length, (consequently, q MUST be at least 160 bits long). When symmetric ciphers stronger than DES are to be used, a larger m may be advisable. p must be a minimum of 512 bits long. } procedure DHGeneratePrivateKeyX(var State: TDHState); var X, M : HugeWord; L, N, I : Integer; begin Assert(State.PrimeQBitCount >= 160); Assert(State.PrimePBitCount >= 512); Assert(not HugeWordIsZero(State.Q)); HugeWordInit(X); HugeWordInit(M); try HugeWordAssign(M, State.Q); //// Use State.PrimeQBitCount instead HugeWordSubtractWord32(M, 2); N := HugeWordGetSizeInBits(M); while (N > 0) and not HugeWordIsBitSet(M, N - 1) do Dec(N); repeat L := (N + HugeWordElementBits - 1) div HugeWordElementBits; SecureHugeWordRandom(X, L); for I := N to L * HugeWordElementBits - 1 do HugeWordClearBit(X, I); until (HugeWordCompareWord32(X, 2) > 0) and (HugeWordCompare(X, M) < 0); HugeWordAssign(State.X, X); finally SecureHugeWordFinalise(M); SecureHugeWordFinalise(X); end; end; { ya is party a's public key; ya = g^xa mod p } procedure DHGeneratePublicKeyY(var State: TDHState); begin Assert(not HugeWordIsZero(State.P)); Assert(not HugeWordIsZero(State.G)); Assert(not HugeWordIsZero(State.X)); HugeWordPowerAndMod(State.Y, State.G, State.X, State.P); end; { Recommended maximum QBitCount for given PBitCount } function DHQBitCount(const PBitCount: Integer): Integer; begin if PBitCount < 512 then raise EDH.Create(SInvalidParameter); if PBitCount <= 2048 then Result := 256 else if PBitCount <= 4096 then Result := 512 else Result := 768; end; { Generate private / public key pair } procedure DHGenerateKeys(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer); begin DHInitHashAlgorithm(State, HashAlgorithm); DHGeneratePrimesPQ(State, PrimeQBitCount, PrimePBitCount, False); DHGenerateG(State); DHGeneratePrivateKeyX(State); DHGeneratePublicKeyY(State); end; { Derive keys from parameters } procedure DHDeriveKeysFromGroupParametersPGQ(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer; const P, G, Q: HugeWord); begin DHInitHashAlgorithm(State, HashAlgorithm); State.PrimeQBitCount := PrimeQBitCount; State.PrimePBitCount := PrimePBitCount; HugeWordAssign(State.P, P); HugeWordAssign(State.G, G); HugeWordAssign(State.Q, Q); DHGeneratePrivateKeyX(State); DHGeneratePublicKeyY(State); end; procedure DHDeriveKeysFromGroupParametersPG(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer; const P, G: HugeWord); begin Assert(PrimeQBitCount >= 160); Assert(PrimePBitCount > PrimeQBitCount); DHInitHashAlgorithm(State, HashAlgorithm); State.PrimePBitCount := PrimePBitCount; DHGeneratePrimeQ(State, PrimeQBitCount, False); HugeWordAssign(State.P, P); HugeWordAssign(State.G, G); DHGeneratePrivateKeyX(State); DHGeneratePublicKeyY(State); end; procedure DHDeriveKeysFromGroupParameterP1(var State: TDHState; const HashAlgorithm: TDHHashAlgorithm; const PrimeQBitCount, PrimePBitCount: Integer; const P: HugeWord); begin Assert(PrimeQBitCount >= 160); Assert(PrimePBitCount > PrimeQBitCount); DHInitHashAlgorithm(State, HashAlgorithm); State.PrimePBitCount := PrimePBitCount; DHGeneratePrimeQ(State, PrimeQBitCount, False); HugeWordAssign(State.P, P); end; procedure DHDeriveKeysFromGroupParameterP2(var State: TDHState); begin DHGeneratePrivateKeyX(State); DHGeneratePublicKeyY(State); end; { X9.42 defines that the shared secret ZZ is generated as follows: ZZ = g ^ (xb * xa) mod p Note that the individual parties actually perform the computations: ZZ = (yb ^ xa) mod p = (ya ^ xb) mod p where ^ denotes exponentiation ya is party a's public key; ya = g ^ xa mod p yb is party b's public key; yb = g ^ xb mod p xa is party a's private key xb is party b's private key } procedure DHGenerateSharedSecretZZ( var State: TDHState; const RemotePublicKeySize: Integer; const RemotePublicKey: HugeWord); begin Assert(RemotePublicKeySize > 0); Assert(not HugeWordIsZero(State.P)); Assert(not HugeWordIsZero(State.X)); Assert(not HugeWordIsZero(RemotePublicKey)); State.RemoteKeySize := RemotePublicKeySize; HugeWordAssign(State.RemoteKey, RemotePublicKey); HugeWordPowerAndMod(State.ZZ, State.RemoteKey, State.X, State.P); end; { Verify that y lies within the interval [2,p-1]. If it does not, the key is invalid. Compute y^q mod p. If the result == 1, the key is valid. Otherwise the key is invalid. } function DHIsPublicKeyValid(const State: TDHState; const Y: HugeWord): Boolean; var T : HugeWord; begin Assert(not HugeWordIsZero(State.P)); Assert(not HugeWordIsZero(State.Q)); if HugeWordCompareWord32(Y, 2) <= 0 then Result := False else if HugeWordCompare(Y, State.P) >= 0 then Result := False else begin HugeWordInit(T); try HugeWordPowerAndMod(T, Y, State.Q, State.P); Result := HugeWordIsOne(T); finally HugeWordFinalise(T); end; end; end; function DHHugeWordKeyEncodeBytes(const A: HugeWord): RawByteString; var L, N, I : Integer; S : RawByteString; P, Q : PByte; begin L := A.Used; if L = 0 then begin Result := ''; exit; end; N := L * HugeWordElementSize; SetLength(S, N); P := A.Data; Inc(P, N - 1); Q := @S[1]; for I := 0 to N - 1 do begin Q^ := P^; Inc(Q); Dec(P); end; I := 1; while (I <= N) and (S[I] = #0) do Inc(I); Dec(I); if I > 0 then if I = N then S := #0 else Delete(S, 1, I); Result := S; end; procedure DHHugeWordKeyDecodeBytes(var A: HugeWord; const B: RawByteString); var L, N, I : Integer; P, Q : PByte; begin L := Length(B); if L = 0 then begin HugeWordAssignZero(A); exit; end; N := (L + HugeWordElementSize - 1) div HugeWordElementSize; HugeWordSetSize(A, N); P := @B[1]; Inc(P, L - 1); Q := A.Data; for I := 0 to L - 1 do begin Q^ := P^; Inc(Q); Dec(P); end; for I := L to N * HugeWordElementSize - 1 do begin Q^ := 0; Inc(Q); end; end; { OtherInfo ::= SEQUENCE [ keyInfo KeySpecificInfo, partyAInfo [0] OCTET STRING OPTIONAL, suppPubInfo [2] OCTET STRING ] KeySpecificInfo ::= SEQUENCE [ algorithm OBJECT IDENTIFIER, counter OCTET STRING SIZE (4..4) ] algorithm is the ASN.1 algorithm OID of the CEK wrapping algorithm with which this KEK will be used. Note that this is NOT an AlgorithmIdentifier, but simply the OBJECT IDENTIFIER. No parameters are used. counter is a 32 bit number, represented in network byte order. Its initial value is 1 for any ZZ, i.e. the byte sequence 00 00 00 01 (hex), and it is incremented by one every time the above key generation function is run for a given KEK. partyAInfo is a random string provided by the sender. In CMS, it is provided as a parameter in the UserKeyingMaterial field (encoded as an OCTET STRING). If provided, partyAInfo MUST contain 512 bits. suppPubInfo is the length of the generated KEK, in bits, represented as a 32 bit number in network byte order. E.g. for 3DES it would be the byte sequence 00 00 00 C0. } function DHGenerateOtherInfo( const CipherOID: array of Integer; const CipherKeyBits: Integer; const KEKCounter: Integer; const PartyAInfo: RawByteString): RawByteString; var S : RawByteString; begin Assert(CipherKeyBits > 0); S := ASN1EncodeSequence( ASN1EncodeOID(CipherOID) + ASN1EncodeInt32AsOctetString(KEKCounter) ); if PartyAInfo <> '' then S := S + ASN1EncodeObj($A0, ASN1EncodeOctetString(PartyAInfo) ); S := S + ASN1EncodeObj($A2, ASN1EncodeInt32AsOctetString(CipherKeyBits) ); Result := ASN1EncodeSequence(S); end; { KM = H ( ZZ || OtherInfo) H is the message digest function SHA-1 [FIPS-180] ZZ is the shared secret value computed in Section 2.1.1. Leading zeros MUST be preserved, so that ZZ occupies as many octets as p. For instance, if p is 1024 bits, ZZ should be 128 bytes long. } procedure DHGenerateKM( var State: TDHState; const CipherOID: array of Integer; const CipherKeyBits: Integer; const KEKCounter: Integer; const PartyAInfo: RawByteString; var KM: T160BitDigest); var S, T : RawByteString; L : Integer; begin Assert(CipherKeyBits > 0); Assert(State.PrimePBitCount > 0); Assert(not HugeWordIsZero(State.ZZ)); // ZZ L := State.PrimePBitCount div 8; SetLength(S, L); HugeWordSetSize(State.ZZ, L div 4); Move(State.ZZ.Data^, S[1], L); // OtherInfo T := S + DHGenerateOtherInfo(CipherOID, CipherKeyBits, KEKCounter, PartyAInfo); // KM KM := CalcSHA1(T); end; { Each key encryption algorithm requires a specific size key (n). The KEK is generated by mapping the left n-most bytes of KM onto the key. For 3DES, which requires 192 bits of keying material, the algorithm must be run twice, once with a counter value of 1 (to generate K1', K2', and the first 32 bits of K3') and once with a counter value of 2 (to generate the last 32 bits of K3). K1',K2' and K3' are then parity adjusted to generate the 3 DES keys K1,K2 and K3. For RC2-128, which requires 128 bits of keying material, the algorithm is run once, with a counter value of 1, and the left-most 128 bits are directly converted to an RC2 key. Similarly, for RC2-40, which requires 40 bits of keying material, the algorithm is run once, with a counter value of 1, and the leftmost 40 bits are used as the key. } procedure DHGenerateKEK( var State: TDHState; const CipherOID: array of Integer; const CipherKeyBits: Integer; const PartyAInfo: RawByteString); var N, I, L : Integer; KEK : RawByteString; KMS : RawByteString; KM : T160BitDigest; begin Assert(CipherKeyBits > 0); Assert(State.HashAlgorithm = dhhSHA1); Assert(State.HashBitCount = 160); KEK := ''; L := CipherKeyBits; N := (L + 159) div 160; for I := 0 to N - 1 do begin DHGenerateKM(State, CipherOID, CipherKeyBits, I + 1, PartyAInfo, KM); SetLength(KMS, 20); Move(KM, KMS[1], 20); if L < 160 then SetLength(KMS, L div 8); KEK := KEK + KMS; Dec(L, 160); end; State.KEK := KEK; end; { } { Test cases } { } {$IFDEF CIPHER_TEST} {$ASSERTIONS ON} procedure Test_OtherInfo; var S : RawByteString; PaI, PaE : RawByteString; begin // RFC 2631 - 2.1.6. Example 1 - OtherInfo encoding S := DHGenerateOtherInfo(OID_3DES_wrap, 192, 1, ''); Assert(S = #$30#$1d + #$30#$13 + #$06#$0b#$2a#$86#$48#$86#$f7#$0d#$01#$09#$10#$03#$06 + #$04#$04 + #$00#$00#$00#$01 + #$a2#$06 + #$04#$04 + #$00#$00#$00#$c0); // RFC 2631 - 2.1.7. Example 2 - OtherInfo encoding PaI := RawByteString( #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01); S := DHGenerateOtherInfo(OID_RC2_wrap, 128, 1, PaI); PaE := RawByteString( #$30#$61 + #$30#$13 + #$06#$0b#$2a#$86#$48#$86#$f7#$0d#$01#$09#$10#$03#$07 + #$04#$04 + #$00#$00#$00#$01 + #$a0#$42 + #$04#$40 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$a2#$06 + #$04#$04 + #$00#$00#$00#$80); Assert(S = PaE); end; procedure Test_KM; var Da : TDHState; S : RawByteString; ZZ : RawByteString; PaI : RawByteString; KM : T160BitDigest; begin // RFC 2631 - 2.1.7. Example 2 - KM calculation DHStateInit(Da); try DHInitHashAlgorithm(Da, dhhSHA1); Da.PrimePBitCount := 160; PaI := RawByteString( #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01 + #$01#$23#$45#$67#$89#$ab#$cd#$ef#$fe#$dc#$ba#$98#$76#$54#$32#$01); ZZ := #$00#$01#$02#$03#$04#$05#$06#$07#$08#$09#$0a#$0b#$0c#$0d#$0e#$0f#$10#$11#$12#$13; HugeWordAssignBufStrB(Da.ZZ, ZZ, False); DHGenerateKM(Da, OID_RC2_wrap, 128, 1, PaI, KM); S := DigestToBufB(KM, 20); Assert(S = #$48#$95#$0c#$46#$e0#$53#$00#$75#$40#$3c#$ce#$72#$88#$96#$04#$e0#$3e#$7b#$5d#$e9); finally DHStateFinalise(Da); end; end; procedure Test_1; const QC = 180; PC = 512; var Da, Db : TDHState; Za, Zb : RawByteString; begin // Test: key generation, shared secret, KEK DHStateInit(Da); DHStateInit(Db); try // generate group parameters and keys for A DHGenerateKeys(Da, dhhSHA1, QC, PC); // validate generated group parameters Assert(DHIsGeneratedGroupParameterValid(Da)); // generate keys for B DHDeriveKeysFromGroupParametersPGQ(Db, dhhSHA1, Da.PrimeQBitCount, Da.PrimePBitCount, Da.P, Da.G, Da.Q); // A validates public keys of A and B Assert(DHIsPublicKeyValid(Da, Da.Y)); Assert(DHIsPublicKeyValid(Da, Db.Y)); // B validates public keys of A and B Assert(DHIsPublicKeyValid(Db, Da.Y)); Assert(DHIsPublicKeyValid(Db, Db.Y)); // A & B generate shared secret from remote public key DHGenerateSharedSecretZZ(Da, PC, Db.Y); DHGenerateSharedSecretZZ(Db, PC, Da.Y); Za := HugeWordToStrB(Da.ZZ); Zb := HugeWordToStrB(Db.ZZ); Assert(Za = Zb); // A & B generate KEK DHGenerateKEK(Da, OID_RC2_wrap, PC, ''); DHGenerateKEK(Db, OID_RC2_wrap, PC, ''); Assert(Length(Da.KEK) = PC div 8); Assert(Length(Db.KEK) = PC div 8); Assert(Da.KEK = Db.KEK); finally DHStateFinalise(Db); DHStateFinalise(Da); end; end; procedure Test_2; var WK : PDHWellKnownGroup; Da, Db : TDHState; Za, Zb : RawByteString; I, PC, QC : Integer; ValP, ValG : HugeWord; begin // Test well known DH groups for I := 0 to 1 do // DHWellKnownGroups - 1 do begin WK := @DHWellKnownGroup[I]; HugeWordInit(ValP); HugeWordInit(ValG); try HexToHugeWordB(WK^.P_Hex, ValP); Assert((HugeWordToHexB(ValP, False) = WK^.P_Hex) or (HugeWordToHexB(ValP, True) = WK^.P_Hex)); Assert(HugeWordIsPrime(ValP) <> pNotPrime); if WK^.G_Hex <> '' then begin HexToHugeWordB(WK^.G_Hex, ValG); Assert((HugeWordToHexB(ValG, False) = WK^.G_Hex) or (HugeWordToHexB(ValG, True) = WK^.G_Hex)); end; PC := WK^.PBitCount; QC := WK^.QBitCount; // Test key derivation and shared secret generation from group params DHStateInit(Da); DHStateInit(Db); try if WK^.G_Hex = '' then begin DHDeriveKeysFromGroupParameterP1(Da, dhhSHA1, QC, PC, ValP); DHInitHashAlgorithm(Db, dhhSHA1); Db.PrimePBitCount := Da.PrimePBitCount; Db.PrimeQBitCount := Da.PrimeQBitCount; HugeWordAssign(Db.Q, Da.Q); HugeWordAssign(Db.P, Da.P); DHGenerateG(Da); HugeWordAssign(Db.G, Da.G); DHDeriveKeysFromGroupParameterP2(Da); DHDeriveKeysFromGroupParameterP2(Db); end else begin DHDeriveKeysFromGroupParametersPG(Da, dhhSHA1, QC, PC, ValP, ValG); DHInitHashAlgorithm(Db, dhhSHA1); Db.PrimePBitCount := Da.PrimePBitCount; Db.PrimeQBitCount := Da.PrimeQBitCount; HugeWordAssign(Db.P, Da.P); HugeWordAssign(Db.G, Da.G); HugeWordAssign(Db.Q, Da.Q); DHGeneratePrivateKeyX(Db); DHGeneratePublicKeyY(Db); end; Assert(not HugeWordEquals(Da.X, Db.X)); Assert(not HugeWordEquals(Da.Y, Db.Y)); //Assert(DHIsPublicKeyValid(Da, Da.Y)); //Assert(DHIsPublicKeyValid(Da, Db.Y)); //Assert(DHIsPublicKeyValid(Db, Da.Y)); //Assert(DHIsPublicKeyValid(Db, Db.Y)); DHGenerateSharedSecretZZ(Da, PC, Db.Y); DHGenerateSharedSecretZZ(Db, PC, Da.Y); Za := HugeWordToStrB(Da.ZZ); Zb := HugeWordToStrB(Db.ZZ); Assert(Za = Zb); DHGenerateKEK(Da, OID_RC2_wrap, QC, ''); DHGenerateKEK(Db, OID_RC2_wrap, QC, ''); Assert(Length(Da.KEK) = QC div 8); Assert(Length(Db.KEK) = QC div 8); Assert(Da.KEK = Db.KEK); finally DHStateFinalise(Db); DHStateFinalise(Da); end; finally HugeWordFinalise(ValG); HugeWordFinalise(ValP); end; end; end; type TTest3Case = record WellKnownIdx : Integer; XA, YA, XB, YB, Z : RawByteString; end; PTest3Case = ^TTest3Case; const Test3Cases = 3; Test3Case: array[0..Test3Cases - 1] of TTest3Case = ( ( // RFC 5114 A.1. WellKnownIdx: 8; XA: 'B9A3B3AE8FEFC1A2930496507086F8455D48943E'; YA: '2A853B3D92197501' + 'B9015B2DEB3ED84F5E021DCC3E52F109D3273D2B7521281C' + 'BABE0E76FF5727FA8ACCE26956BA9A1FCA26F20228D8693F' + 'EB10841D84A7360054ECE5A7F5B7A61AD3DFB3C60D2E4310' + '6D8727DA37DF9CCE95B478755D06BCEA8F9D45965F75A5F3' + 'D1DF3701165FC9E50C4279CEB07F989540AE96D5D88ED776'; XB: '9392C9F9EB6A7A6A9022F7D83E7223C6835BBDDA'; YB: '717A6CB053371FF4' + 'A3B932941C1E5663F861A1D6AD34AE66576DFB98F6C6CBF9' + 'DDD5A56C7833F6BCFDFF095582AD868E440E8D09FD769E3C' + 'ECCDC3D3B1E4CFA057776CAAF9739B6A9FEE8E7411F8D6DA' + 'C09D6A4EDB46CC2B5D5203090EAE6126311E53FD2C14B574' + 'E6A3109A3DA1BE41BDCEAA186F5CE06716A2B6A07B3C33FE'; Z : '5C804F454D30D9C4' + 'DF85271F93528C91DF6B48AB5F80B3B59CAAC1B28F8ACBA9' + 'CD3E39F3CB614525D9521D2E644C53B807B810F340062F25' + '7D7D6FBFE8D5E8F072E9B6E9AFDA9413EAFB2E8B0699B1FB' + '5A0CACEDDEAEAD7E9CFBB36AE2B420835BD83A19FB0B5E96' + 'BF8FA4D09E345525167ECD9155416F46F408ED31B63C6E6D' ), ( // RFC 5114 A.2. WellKnownIdx: 9; XA: '22E62601' + 'DBFFD06708A680F747F361F76D8F4F721A0548E483294B0C'; YA: '1B3A63451BD886E699E67B494E288BD7' + 'F8E0D370BADDA7A0EFD2FDE7D8F66145CC9F280419975EB8' + '08877C8A4C0C8E0BD48D4A5401EB1E8776BFEEE134C03831' + 'AC273CD9D635AB0CE006A42A887E3F52FB8766B650F38078' + 'BC8EE8580CEFE243968CFC4F8DC3DB084554171D41BF2E86' + '1B7BB4D69DD0E01EA387CBAA5CA672AFCBE8BDB9D62D4CE1' + '5F17DD36F91ED1EEDD65CA4A06455CB94CD40A52EC360E84' + 'B3C926E22C4380A3BF309D56849768B7F52CFDF655FD053A' + '7EF706979E7E5806B17DFAE53AD2A5BC568EBB529A7A61D6' + '8D256F8FC97C074A861D827E2EBC8C6134553115B70E7103' + '920AA16D85E52BCBAB8D786A68178FA8FF7C2F5C71648D6F'; XB: '4FF3BC96' + 'C7FC6A6D71D3B363800A7CDFEF6FC41B4417EA15353B7590'; YB: '4DCEE992A9762A13F2F83844AD3D77EE' + '0E31C9718B3DB6C2035D3961182C3E0BA247EC4182D760CD' + '48D99599970622A1881BBA2DC822939C78C3912C6661FA54' + '38B20766222B75E24C2E3AD0C7287236129525EE15B5DD79' + '98AA04C4A9696CACD7172083A97A81664EAD2C479E444E4C' + '0654CC19E28D7703CEE8DACD6126F5D665EC52C67255DB92' + '014B037EB621A2AC8E365DE071FFC1400ACF077A12913DD8' + 'DE89473437AB7BA346743C1B215DD9C12164A7E4053118D1' + '99BEC8EF6FC561170C84C87D10EE9A674A1FA8FFE13BDFBA' + '1D44DE48946D68DC0CDD777635A7AB5BFB1E4BB7B856F968' + '27734C184138E915D9C3002EBCE53120546A7E2002142B6C'; Z : '34D9BDDC1B42176C313FEA034C21034D' + '074A6313BB4ECDB3703FFF424567A46BDF75530EDE0A9DA5' + '229DE7D76732286CBC0F91DA4C3C852FC099C679531D94C7' + '8AB03D9DECB0A4E4CA8B2BB4591C4021CF8CE3A20A541D33' + '994017D0200AE2C9516E2FF5145779269E862B0FB474A2D5' + '6DC31ED569A7700B4C4AB16B22A45513531EF523D7121207' + '7B5A169BDEFFAD7AD9608284C7795B6D5A5183B87066DE17' + 'D8D671C9EBD8EC89544D45EC061593D442C62AB9CE3B1CB9' + '943A1D23A5EA3BCF21A01471E67E003E7F8A69C728BE490B' + '2FC88CFEB92DB6A215E5D03C17C464C9AC1A46E203E13F95' + '2995FB03C69D3CC47FCB510B6998FFD3AA6DE73CF9F63869'; ), ( // RFC 5114 A.3. WellKnownIdx: 10; XA: '0881382CDB87660C' + '6DC13E614938D5B9C8B2F248581CC5E31B35454397FCE50E'; YA: '2E9380C8323AF97545BC4941DEB0EC37' + '42C62FE0ECE824A6ABDBE66C59BEE0242911BFB967235CEB' + 'A35AE13E4EC752BE630B92DC4BDE2847A9C62CB815274542' + '1FB7EB60A63C0FE9159FCCE726CE7CD8523D7450667EF840' + 'E4919121EB5F01C8C9B0D3D648A93BFB75689E8244AC134A' + 'F544711CE79A02DCC34226684780DDDCB498594106C37F5B' + 'C79856487AF5AB022A2E5E42F09897C1A85A11EA0212AF04' + 'D9B4CEBC937C3C1A3E15A8A0342E337615C84E7FE3B8B9B8' + '7FB1E73A15AF12A30D746E06DFC34F290D797CE51AA13AA7' + '85BF6658AFF5E4B093003CBEAF665B3C2E113A3A4E905269' + '341DC0711426685F4EF37E868A8126FF3F2279B57CA67E29'; XB: '7D62A7E3EF36DE61' + '7B13D1AFB82C780D83A23BD4EE6705645121F371F546A53D'; YB: '575F0351BD2B1B817448BDF87A6C362C' + '1E289D3903A30B9832C5741FA250363E7ACBC7F77F3DACBC' + '1F131ADD8E03367EFF8FBBB3E1C5784424809B25AFE4D226' + '2A1A6FD2FAB64105CA30A674E07F7809852088632FC04923' + '3791AD4EDD083A978B883EE618BC5E0DD047415F2D95E683' + 'CF14826B5FBE10D3CE41C6C120C78AB20008C698BF7F0BCA' + 'B9D7F407BED0F43AFB2970F57F8D12043963E66DDD320D59' + '9AD9936C8F44137C08B180EC5E985CEBE186F3D549677E80' + '607331EE17AF3380A725B0782317D7DD43F59D7AF9568A9B' + 'B63A84D365F92244ED120988219302F42924C7CA90B89D24' + 'F71B0AB697823D7DEB1AFF5B0E8E4A45D49F7F53757E1913'; Z : '86C70BF8D0BB81BB01078A17219CB7D2' + '7203DB2A19C877F1D1F19FD7D77EF22546A68F005AD52DC8' + '4553B78FC60330BE51EA7C0672CAC1515E4B35C047B9A551' + 'B88F39DC26DA14A09EF74774D47C762DD177F9ED5BC2F11E' + '52C879BD95098504CD9EECD8A8F9B3EFBD1F008AC5853097' + 'D9D1837F2B18F77CD7BE01AF80A7C7B5EA3CA54CC02D0C11' + '6FEE3F95BB87399385875D7E86747E676E728938ACBFF709' + '8E05BE4DCFB24052B83AEFFB14783F029ADBDE7F53FAE920' + '84224090E007CEE94D4BF2BACE9FFD4B57D2AF7C724D0CAA' + '19BF0501F6F17B4AA10F425E3EA76080B4B9D6B3CEFEA115' + 'B2CEB8789BB8A3B0EA87FEBE63B6C8F846EC6DB0C26C5D7C'; ) ); procedure Test_3; var Tst : PTest3Case; WK : PDHWellKnownGroup; Da, Db : TDHState; Za, Zb, Zz : RawByteString; I, PC, QC : Integer; ValP, ValG : HugeWord; begin for I := 0 to Test3Cases - 1 do begin Tst := @Test3Case[I]; WK := @DHWellKnownGroup[Tst^.WellKnownIdx]; HugeWordInit(ValP); HugeWordInit(ValG); try HexToHugeWordB(WK^.P_Hex, ValP); HexToHugeWordB(WK^.G_Hex, ValG); PC := WK^.PBitCount; QC := WK^.QBitCount; // Test shared secret generation using test keys DHStateInit(Da); DHStateInit(Db); try DHInitHashAlgorithm(Da, dhhSHA1); HugeWordAssign(Da.P, ValP); HugeWordAssign(Da.G, ValG); Da.PrimePBitCount := PC; Da.PrimeQBitCount := QC; HexToHugeWordB(Tst^.XA, Da.X); HexToHugeWordB(Tst^.YA, Da.Y); DHInitHashAlgorithm(Db, dhhSHA1); HugeWordAssign(Db.P, ValP); HugeWordAssign(Db.G, ValG); Db.PrimePBitCount := PC; Db.PrimeQBitCount := QC; HexToHugeWordB(Tst^.XB, Db.X); HexToHugeWordB(Tst^.YB, Db.Y); DHGenerateSharedSecretZZ(Da, PC, Db.Y); DHGenerateSharedSecretZZ(Db, PC, Da.Y); Za := HugeWordToStrB(Da.ZZ); Zb := HugeWordToStrB(Db.ZZ); Assert(Za = Zb); Zz := HugeWordToHexB(Da.ZZ, False); Assert(Zz = Tst^.Z); DHGenerateKEK(Da, OID_RC2_wrap, QC, ''); DHGenerateKEK(Db, OID_RC2_wrap, QC, ''); Assert(Length(Da.KEK) = QC div 8); Assert(Length(Db.KEK) = QC div 8); Assert(Da.KEK = Db.KEK); finally DHStateFinalise(Db); DHStateFinalise(Da); end; finally HugeWordFinalise(ValG); HugeWordFinalise(ValP); end; end; end; procedure Test; begin Test_OtherInfo; Test_KM; Test_1; Test_2; Test_3; end; {$ENDIF} end.