/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.security.keystore;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
import android.security.keymaster.KeymasterDefs;

import libcore.util.EmptyArray;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.Locale;

/**
 * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
 */
public abstract class KeyProperties {
    private KeyProperties() {}

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true,
            value = {
                PURPOSE_ENCRYPT,
                PURPOSE_DECRYPT,
                PURPOSE_SIGN,
                PURPOSE_VERIFY,
                })
    public @interface PurposeEnum {}

    /**
     * Purpose of key: encryption.
     */
    public static final int PURPOSE_ENCRYPT = 1 << 0;

    /**
     * Purpose of key: decryption.
     */
    public static final int PURPOSE_DECRYPT = 1 << 1;

    /**
     * Purpose of key: signing or generating a Message Authentication Code (MAC).
     */
    public static final int PURPOSE_SIGN = 1 << 2;

    /**
     * Purpose of key: signature or Message Authentication Code (MAC) verification.
     */
    public static final int PURPOSE_VERIFY = 1 << 3;

    /**
     * @hide
     */
    public static abstract class Purpose {
        private Purpose() {}

        public static int toKeymaster(@PurposeEnum int purpose) {
            switch (purpose) {
                case PURPOSE_ENCRYPT:
                    return KeymasterDefs.KM_PURPOSE_ENCRYPT;
                case PURPOSE_DECRYPT:
                    return KeymasterDefs.KM_PURPOSE_DECRYPT;
                case PURPOSE_SIGN:
                    return KeymasterDefs.KM_PURPOSE_SIGN;
                case PURPOSE_VERIFY:
                    return KeymasterDefs.KM_PURPOSE_VERIFY;
                default:
                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
            }
        }

        public static @PurposeEnum int fromKeymaster(int purpose) {
            switch (purpose) {
                case KeymasterDefs.KM_PURPOSE_ENCRYPT:
                    return PURPOSE_ENCRYPT;
                case KeymasterDefs.KM_PURPOSE_DECRYPT:
                    return PURPOSE_DECRYPT;
                case KeymasterDefs.KM_PURPOSE_SIGN:
                    return PURPOSE_SIGN;
                case KeymasterDefs.KM_PURPOSE_VERIFY:
                    return PURPOSE_VERIFY;
                default:
                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
            }
        }

        @NonNull
        public static int[] allToKeymaster(@PurposeEnum int purposes) {
            int[] result = getSetFlags(purposes);
            for (int i = 0; i < result.length; i++) {
                result[i] = toKeymaster(result[i]);
            }
            return result;
        }

        public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
            @PurposeEnum int result = 0;
            for (int keymasterPurpose : purposes) {
                result |= fromKeymaster(keymasterPurpose);
            }
            return result;
        }
    }

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @StringDef({
        KEY_ALGORITHM_RSA,
        KEY_ALGORITHM_EC,
        KEY_ALGORITHM_AES,
        KEY_ALGORITHM_HMAC_SHA1,
        KEY_ALGORITHM_HMAC_SHA224,
        KEY_ALGORITHM_HMAC_SHA256,
        KEY_ALGORITHM_HMAC_SHA384,
        KEY_ALGORITHM_HMAC_SHA512,
        })
    public @interface KeyAlgorithmEnum {}

    /** Rivest Shamir Adleman (RSA) key. */
    public static final String KEY_ALGORITHM_RSA = "RSA";

    /** Elliptic Curve (EC) Cryptography key. */
    public static final String KEY_ALGORITHM_EC = "EC";

    /** Advanced Encryption Standard (AES) key. */
    public static final String KEY_ALGORITHM_AES = "AES";

    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
    public static final String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";

    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
    public static final String KEY_ALGORITHM_HMAC_SHA224 = "HmacSHA224";

    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
    public static final String KEY_ALGORITHM_HMAC_SHA256 = "HmacSHA256";

    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
    public static final String KEY_ALGORITHM_HMAC_SHA384 = "HmacSHA384";

    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
    public static final String KEY_ALGORITHM_HMAC_SHA512 = "HmacSHA512";

    /**
     * @hide
     */
    public static abstract class KeyAlgorithm {
        private KeyAlgorithm() {}

        public static int toKeymasterAsymmetricKeyAlgorithm(
                @NonNull @KeyAlgorithmEnum String algorithm) {
            if (KEY_ALGORITHM_EC.equalsIgnoreCase(algorithm)) {
                return KeymasterDefs.KM_ALGORITHM_EC;
            } else if (KEY_ALGORITHM_RSA.equalsIgnoreCase(algorithm)) {
                return KeymasterDefs.KM_ALGORITHM_RSA;
            } else {
                throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
            }
        }

        @NonNull
        public static @KeyAlgorithmEnum String fromKeymasterAsymmetricKeyAlgorithm(
                int keymasterAlgorithm) {
            switch (keymasterAlgorithm) {
                case KeymasterDefs.KM_ALGORITHM_EC:
                    return KEY_ALGORITHM_EC;
                case KeymasterDefs.KM_ALGORITHM_RSA:
                    return KEY_ALGORITHM_RSA;
                default:
                    throw new IllegalArgumentException(
                            "Unsupported key algorithm: " + keymasterAlgorithm);
            }
        }

        public static int toKeymasterSecretKeyAlgorithm(
                @NonNull @KeyAlgorithmEnum String algorithm) {
            if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
                return KeymasterDefs.KM_ALGORITHM_AES;
            } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
                return KeymasterDefs.KM_ALGORITHM_HMAC;
            } else {
                throw new IllegalArgumentException(
                        "Unsupported secret key algorithm: " + algorithm);
            }
        }

        @NonNull
        public static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
                int keymasterAlgorithm, int keymasterDigest) {
            switch (keymasterAlgorithm) {
                case KeymasterDefs.KM_ALGORITHM_AES:
                    return KEY_ALGORITHM_AES;
                case KeymasterDefs.KM_ALGORITHM_HMAC:
                    switch (keymasterDigest) {
                        case KeymasterDefs.KM_DIGEST_SHA1:
                            return KEY_ALGORITHM_HMAC_SHA1;
                        case KeymasterDefs.KM_DIGEST_SHA_2_224:
                            return KEY_ALGORITHM_HMAC_SHA224;
                        case KeymasterDefs.KM_DIGEST_SHA_2_256:
                            return KEY_ALGORITHM_HMAC_SHA256;
                        case KeymasterDefs.KM_DIGEST_SHA_2_384:
                            return KEY_ALGORITHM_HMAC_SHA384;
                        case KeymasterDefs.KM_DIGEST_SHA_2_512:
                            return KEY_ALGORITHM_HMAC_SHA512;
                        default:
                            throw new IllegalArgumentException("Unsupported HMAC digest: "
                                    + Digest.fromKeymaster(keymasterDigest));
                    }
                default:
                    throw new IllegalArgumentException(
                            "Unsupported key algorithm: " + keymasterAlgorithm);
            }
        }

        /**
         * @hide
         *
         * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
         */
        public static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum String algorithm) {
            String algorithmUpper = algorithm.toUpperCase(Locale.US);
            if (algorithmUpper.startsWith("HMAC")) {
                String digestUpper = algorithmUpper.substring("HMAC".length());
                switch (digestUpper) {
                    case "SHA1":
                        return KeymasterDefs.KM_DIGEST_SHA1;
                    case "SHA224":
                        return KeymasterDefs.KM_DIGEST_SHA_2_224;
                    case "SHA256":
                        return KeymasterDefs.KM_DIGEST_SHA_2_256;
                    case "SHA384":
                        return KeymasterDefs.KM_DIGEST_SHA_2_384;
                    case "SHA512":
                        return KeymasterDefs.KM_DIGEST_SHA_2_512;
                    default:
                        throw new IllegalArgumentException(
                                "Unsupported HMAC digest: " + digestUpper);
                }
            } else {
                return -1;
            }
        }
    }

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @StringDef({
        BLOCK_MODE_ECB,
        BLOCK_MODE_CBC,
        BLOCK_MODE_CTR,
        BLOCK_MODE_GCM,
        })
    public @interface BlockModeEnum {}

    /** Electronic Codebook (ECB) block mode. */
    public static final String BLOCK_MODE_ECB = "ECB";

    /** Cipher Block Chaining (CBC) block mode. */
    public static final String BLOCK_MODE_CBC = "CBC";

    /** Counter (CTR) block mode. */
    public static final String BLOCK_MODE_CTR = "CTR";

    /** Galois/Counter Mode (GCM) block mode. */
    public static final String BLOCK_MODE_GCM = "GCM";

    /**
     * @hide
     */
    public static abstract class BlockMode {
        private BlockMode() {}

        public static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
            if (BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
                return KeymasterDefs.KM_MODE_ECB;
            } else if (BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
                return KeymasterDefs.KM_MODE_CBC;
            } else if (BLOCK_MODE_CTR.equalsIgnoreCase(blockMode)) {
                return KeymasterDefs.KM_MODE_CTR;
            } else if (BLOCK_MODE_GCM.equalsIgnoreCase(blockMode)) {
                return KeymasterDefs.KM_MODE_GCM;
            } else {
                throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
            }
        }

        @NonNull
        public static @BlockModeEnum String fromKeymaster(int blockMode) {
            switch (blockMode) {
                case KeymasterDefs.KM_MODE_ECB:
                    return BLOCK_MODE_ECB;
                case KeymasterDefs.KM_MODE_CBC:
                    return BLOCK_MODE_CBC;
                case KeymasterDefs.KM_MODE_CTR:
                    return BLOCK_MODE_CTR;
                case KeymasterDefs.KM_MODE_GCM:
                    return BLOCK_MODE_GCM;
                default:
                    throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
            }
        }

        @NonNull
        public static @BlockModeEnum String[] allFromKeymaster(
                @NonNull Collection<Integer> blockModes) {
            if ((blockModes == null) || (blockModes.isEmpty())) {
                return EmptyArray.STRING;
            }
            @BlockModeEnum String[] result = new String[blockModes.size()];
            int offset = 0;
            for (int blockMode : blockModes) {
                result[offset] = fromKeymaster(blockMode);
                offset++;
            }
            return result;
        }

        public static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
            if ((blockModes == null) || (blockModes.length == 0)) {
                return EmptyArray.INT;
            }
            int[] result = new int[blockModes.length];
            for (int i = 0; i < blockModes.length; i++) {
                result[i] = toKeymaster(blockModes[i]);
            }
            return result;
        }
    }

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @StringDef({
        ENCRYPTION_PADDING_NONE,
        ENCRYPTION_PADDING_PKCS7,
        ENCRYPTION_PADDING_RSA_PKCS1,
        ENCRYPTION_PADDING_RSA_OAEP,
        })
    public @interface EncryptionPaddingEnum {}

    /**
     * No encryption padding.
     */
    public static final String ENCRYPTION_PADDING_NONE = "NoPadding";

    /**
     * PKCS#7 encryption padding scheme.
     */
    public static final String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";

    /**
     * RSA PKCS#1 v1.5 padding scheme for encryption.
     */
    public static final String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";

    /**
     * RSA Optimal Asymmetric Encryption Padding (OAEP) scheme.
     */
    public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";

    /**
     * @hide
     */
    public static abstract class EncryptionPadding {
        private EncryptionPadding() {}

        public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
            if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
                return KeymasterDefs.KM_PAD_NONE;
            } else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
                return KeymasterDefs.KM_PAD_PKCS7;
            } else if (ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
                return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
            } else if (ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
                return KeymasterDefs.KM_PAD_RSA_OAEP;
            } else {
                throw new IllegalArgumentException(
                        "Unsupported encryption padding scheme: " + padding);
            }
        }

        @NonNull
        public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
            switch (padding) {
                case KeymasterDefs.KM_PAD_NONE:
                    return ENCRYPTION_PADDING_NONE;
                case KeymasterDefs.KM_PAD_PKCS7:
                    return ENCRYPTION_PADDING_PKCS7;
                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
                    return ENCRYPTION_PADDING_RSA_PKCS1;
                case KeymasterDefs.KM_PAD_RSA_OAEP:
                    return ENCRYPTION_PADDING_RSA_OAEP;
                default:
                    throw new IllegalArgumentException(
                            "Unsupported encryption padding: " + padding);
            }
        }

        @NonNull
        public static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
            if ((paddings == null) || (paddings.length == 0)) {
                return EmptyArray.INT;
            }
            int[] result = new int[paddings.length];
            for (int i = 0; i < paddings.length; i++) {
                result[i] = toKeymaster(paddings[i]);
            }
            return result;
        }
    }

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @StringDef({
        SIGNATURE_PADDING_RSA_PKCS1,
        SIGNATURE_PADDING_RSA_PSS,
        })
    public @interface SignaturePaddingEnum {}

    /**
     * RSA PKCS#1 v1.5 padding for signatures.
     */
    public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";

    /**
     * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
     */
    public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";

    static abstract class SignaturePadding {
        private SignaturePadding() {}

        static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
            switch (padding.toUpperCase(Locale.US)) {
                case SIGNATURE_PADDING_RSA_PKCS1:
                    return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
                case SIGNATURE_PADDING_RSA_PSS:
                    return KeymasterDefs.KM_PAD_RSA_PSS;
                default:
                    throw new IllegalArgumentException(
                            "Unsupported signature padding scheme: " + padding);
            }
        }

        @NonNull
        static @SignaturePaddingEnum String fromKeymaster(int padding) {
            switch (padding) {
                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
                    return SIGNATURE_PADDING_RSA_PKCS1;
                case KeymasterDefs.KM_PAD_RSA_PSS:
                    return SIGNATURE_PADDING_RSA_PSS;
                default:
                    throw new IllegalArgumentException("Unsupported signature padding: " + padding);
            }
        }

        @NonNull
        static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) {
            if ((paddings == null) || (paddings.length == 0)) {
                return EmptyArray.INT;
            }
            int[] result = new int[paddings.length];
            for (int i = 0; i < paddings.length; i++) {
                result[i] = toKeymaster(paddings[i]);
            }
            return result;
        }
    }

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @StringDef({
        DIGEST_NONE,
        DIGEST_MD5,
        DIGEST_SHA1,
        DIGEST_SHA224,
        DIGEST_SHA256,
        DIGEST_SHA384,
        DIGEST_SHA512,
        })
    public @interface DigestEnum {}

    /**
     * No digest: sign/authenticate the raw message.
     */
    public static final String DIGEST_NONE = "NONE";

    /**
     * MD5 digest.
     */
    public static final String DIGEST_MD5 = "MD5";

    /**
     * SHA-1 digest.
     */
    public static final String DIGEST_SHA1 = "SHA-1";

    /**
     * SHA-2 224 (aka SHA-224) digest.
     */
    public static final String DIGEST_SHA224 = "SHA-224";

    /**
     * SHA-2 256 (aka SHA-256) digest.
     */
    public static final String DIGEST_SHA256 = "SHA-256";

    /**
     * SHA-2 384 (aka SHA-384) digest.
     */
    public static final String DIGEST_SHA384 = "SHA-384";

    /**
     * SHA-2 512 (aka SHA-512) digest.
     */
    public static final String DIGEST_SHA512 = "SHA-512";

    /**
     * @hide
     */
    public static abstract class Digest {
        private Digest() {}

        public static int toKeymaster(@NonNull @DigestEnum String digest) {
            switch (digest.toUpperCase(Locale.US)) {
                case DIGEST_SHA1:
                    return KeymasterDefs.KM_DIGEST_SHA1;
                case DIGEST_SHA224:
                    return KeymasterDefs.KM_DIGEST_SHA_2_224;
                case DIGEST_SHA256:
                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
                case DIGEST_SHA384:
                    return KeymasterDefs.KM_DIGEST_SHA_2_384;
                case DIGEST_SHA512:
                    return KeymasterDefs.KM_DIGEST_SHA_2_512;
                case DIGEST_NONE:
                    return KeymasterDefs.KM_DIGEST_NONE;
                case DIGEST_MD5:
                    return KeymasterDefs.KM_DIGEST_MD5;
                default:
                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
            }
        }

        @NonNull
        public static @DigestEnum String fromKeymaster(int digest) {
            switch (digest) {
                case KeymasterDefs.KM_DIGEST_NONE:
                    return DIGEST_NONE;
                case KeymasterDefs.KM_DIGEST_MD5:
                    return DIGEST_MD5;
                case KeymasterDefs.KM_DIGEST_SHA1:
                    return DIGEST_SHA1;
                case KeymasterDefs.KM_DIGEST_SHA_2_224:
                    return DIGEST_SHA224;
                case KeymasterDefs.KM_DIGEST_SHA_2_256:
                    return DIGEST_SHA256;
                case KeymasterDefs.KM_DIGEST_SHA_2_384:
                    return DIGEST_SHA384;
                case KeymasterDefs.KM_DIGEST_SHA_2_512:
                    return DIGEST_SHA512;
                default:
                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
            }
        }

        @NonNull
        public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
            switch (digest) {
                case KeymasterDefs.KM_DIGEST_NONE:
                    return "NONE";
                case KeymasterDefs.KM_DIGEST_MD5:
                    return "MD5";
                case KeymasterDefs.KM_DIGEST_SHA1:
                    return "SHA1";
                case KeymasterDefs.KM_DIGEST_SHA_2_224:
                    return "SHA224";
                case KeymasterDefs.KM_DIGEST_SHA_2_256:
                    return "SHA256";
                case KeymasterDefs.KM_DIGEST_SHA_2_384:
                    return "SHA384";
                case KeymasterDefs.KM_DIGEST_SHA_2_512:
                    return "SHA512";
                default:
                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
            }
        }

        @NonNull
        public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
            if (digests.isEmpty()) {
                return EmptyArray.STRING;
            }
            String[] result = new String[digests.size()];
            int offset = 0;
            for (int digest : digests) {
                result[offset] = fromKeymaster(digest);
                offset++;
            }
            return result;
        }

        @NonNull
        public static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
            if ((digests == null) || (digests.length == 0)) {
                return EmptyArray.INT;
            }
            int[] result = new int[digests.length];
            int offset = 0;
            for (@DigestEnum String digest : digests) {
                result[offset] = toKeymaster(digest);
                offset++;
            }
            return result;
        }
    }

    /**
     * @hide
     */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({
        ORIGIN_GENERATED,
        ORIGIN_IMPORTED,
        ORIGIN_UNKNOWN,
        })
    public @interface OriginEnum {}

    /** Key was generated inside AndroidKeyStore. */
    public static final int ORIGIN_GENERATED = 1 << 0;

    /** Key was imported into AndroidKeyStore. */
    public static final int ORIGIN_IMPORTED = 1 << 1;

    /**
     * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
     * implementation which does not record origin information.
     */
    public static final int ORIGIN_UNKNOWN = 1 << 2;

    /**
     * @hide
     */
    public static abstract class Origin {
        private Origin() {}

        public static @OriginEnum int fromKeymaster(int origin) {
            switch (origin) {
                case KeymasterDefs.KM_ORIGIN_GENERATED:
                    return ORIGIN_GENERATED;
                case KeymasterDefs.KM_ORIGIN_IMPORTED:
                    return ORIGIN_IMPORTED;
                case KeymasterDefs.KM_ORIGIN_UNKNOWN:
                    return ORIGIN_UNKNOWN;
                default:
                    throw new IllegalArgumentException("Unknown origin: " + origin);
            }
        }
    }

    private static int[] getSetFlags(int flags) {
        if (flags == 0) {
            return EmptyArray.INT;
        }
        int result[] = new int[getSetBitCount(flags)];
        int resultOffset = 0;
        int flag = 1;
        while (flags != 0) {
            if ((flags & 1) != 0) {
                result[resultOffset] = flag;
                resultOffset++;
            }
            flags >>>= 1;
            flag <<= 1;
        }
        return result;
    }

    private static int getSetBitCount(int value) {
        if (value == 0) {
            return 0;
        }
        int result = 0;
        while (value != 0) {
            if ((value & 1) != 0) {
                result++;
            }
            value >>>= 1;
        }
        return result;
    }
}
