All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jcifs.pac.PacMac Maven / Gradle / Ivy

/*
 * © 2017 AgNO3 Gmbh & Co. KG
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package jcifs.pac;


import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.kerberos.KerberosKey;


@SuppressWarnings ( "javadoc" )
public class PacMac {

    /**
     * 
     */
    private static final String HMAC_KEY = "HMAC";
    private static final byte[] MD5_CONSTANT = "signaturekey\0".getBytes(StandardCharsets.US_ASCII);
    private static final byte[] ZERO_IV = new byte[] {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };


    public static byte[] calculateMacArcfourHMACMD5 ( int keyusage, Key key, byte[] data ) throws GeneralSecurityException {
        int ms_usage = mapArcfourMD5KeyUsage(keyusage);
        Mac mac = Mac.getInstance("HmacMD5");
        MessageDigest md = MessageDigest.getInstance("MD5");
        mac.init(key);
        byte[] dk = mac.doFinal(MD5_CONSTANT);
        try {
            // little endian
            md.update((byte) ( ms_usage & 0xFF ));
            md.update((byte) ( ( ms_usage >> 8 ) & 0xFF ));
            md.update((byte) ( ( ms_usage >> 16 ) & 0xFF ));
            md.update((byte) ( ( ms_usage >> 24 ) & 0xFF ));
            byte[] dgst = md.digest(data);
            mac.reset();
            mac.init(new SecretKeySpec(dk, HMAC_KEY));
            return mac.doFinal(dgst);
        }
        finally {
            Arrays.fill(dk, 0, dk.length, (byte) 0);
        }
    }


    private static int mapArcfourMD5KeyUsage ( int keyusage ) {
        int ms_usage = keyusage;
        switch ( ms_usage ) {
        case 3:
            ms_usage = 8;
        case 9:
            ms_usage = 8;
        case 23:
            ms_usage = 13;
        }
        return ms_usage;
    }


    public static byte[] calculateMacHMACAES ( int usage, KerberosKey baseKey, byte[] input ) throws GeneralSecurityException {
        byte[] cst = new byte[] {
            (byte) ( ( usage >> 24 ) & 0xFF ), (byte) ( ( usage >> 16 ) & 0xFF ), (byte) ( ( usage >> 8 ) & 0xFF ), (byte) ( usage & 0xFF ),
            (byte) 0x99
        };

        byte[] output = new byte[12];
        byte[] dk = deriveKeyAES(baseKey, cst); // Checksum key
        try {
            Mac m = Mac.getInstance("HmacSHA1");
            m.init(new SecretKeySpec(dk, HMAC_KEY));
            System.arraycopy(m.doFinal(input), 0, output, 0, 12);
            return output;
        }
        finally {
            Arrays.fill(dk, 0, dk.length, (byte) 0);
        }
    }


    public static byte[] deriveKeyAES ( KerberosKey key, byte[] constant ) throws GeneralSecurityException {
        byte[] keybytes = key.getEncoded();
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keybytes, "AES"), new IvParameterSpec(ZERO_IV, 0, ZERO_IV.length));
        if ( constant.length != cipher.getBlockSize() ) {
            constant = expandNFold(constant, cipher.getBlockSize());
        }
        byte[] enc = constant;
        int klen = keybytes.length;
        byte[] dk = new byte[klen];
        for ( int n = 0; n < klen; ) {
            byte[] block = cipher.doFinal(enc);
            int len = Math.min(klen - n, block.length);
            System.arraycopy(block, 0, dk, n, len);
            n += len;
            enc = block;
        }
        return dk;
    }


    public static byte[] expandNFold ( byte[] data, int outlen ) {
        int lcm = lcm(outlen, data.length);
        byte[] buf = new byte[outlen];
        int carry = 0;
        for ( int i = lcm - 1; i >= 0; i-- ) {
            carry = carry_add(data, buf, carry, i);
        }

        if ( carry != 0 ) {
            for ( int i = outlen - 1; i >= 0; i-- ) {
                carry += ( buf[ i ] & 0xff );
                buf[ i ] = (byte) ( carry & 0xff );
                carry >>>= 8;
            }
        }
        return buf;
    }


    private static int carry_add ( byte[] data, byte[] out, int c, int i ) {
        int ilen = data.length, olen = out.length;
        int msbit = ( ( ( ilen << 3 ) - 1 ) + ( ( ( ilen << 3 ) + 13 ) * ( i / ilen ) ) + ( ( ilen - ( i % ilen ) ) << 3 ) ) % ( ilen << 3 );
        int mshigh = msbit >>> 3, mslow = msbit & 7;
        int b = c + ( out[ i % olen ] & 0xff ) + ( ( ( ( ( data[ ( ( ilen - 1 ) - mshigh ) % ilen ] & 0xff ) << 8 )
                | ( data[ ( ilen - mshigh ) % ilen ] & 0xff ) ) >>> ( mslow + 1 ) ) & 0xff );
        out[ i % olen ] = (byte) ( b & 0xff );
        return b >>>= 8;
    }


    private static int lcm ( int u, int v ) {
        int a = u, b = v, c;
        while ( b != 0 ) {
            c = b;
            b = a % b;
            a = c;
        }
        return u * v / a;
    }


    /**
     * @param type
     * @param keys
     * @return the calculated mac
     * @throws PACDecodingException
     */
    public static byte[] calculateMac ( int type, Map keys, byte[] data ) throws PACDecodingException {
        try {
            int usage = 17;
            if ( type == PacSignature.KERB_CHECKSUM_HMAC_MD5 ) {
                KerberosKey key = keys.get(PacSignature.ETYPE_ARCFOUR_HMAC);
                if ( key == null ) {
                    throw new PACDecodingException("Missing key");
                }
                return calculateMacArcfourHMACMD5(usage, key, data);
            }
            else if ( type == PacSignature.HMAC_SHA1_96_AES128 || type == PacSignature.HMAC_SHA1_96_AES256 ) {
                KerberosKey key = type == PacSignature.HMAC_SHA1_96_AES128 ? keys.get(PacSignature.ETYPE_AES128_CTS_HMAC_SHA1_96)
                        : keys.get(PacSignature.ETYPE_AES256_CTS_HMAC_SHA1_96);
                if ( key == null ) {
                    throw new PACDecodingException("Missing key");
                }
                return calculateMacHMACAES(usage, key, data);
            }
            else {
                throw new PACDecodingException("Invalid MAC algorithm");
            }
        }
        catch ( GeneralSecurityException e ) {
            throw new PACDecodingException("Failed to calculate MAC", e);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy