KeyProperties.java revision 3f8d4d840894468f2be8a5b56ff266cef2d71c50
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.security.keystore;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.annotation.StringDef;
23import android.security.keymaster.KeymasterDefs;
24
25import libcore.util.EmptyArray;
26
27import java.lang.annotation.Retention;
28import java.lang.annotation.RetentionPolicy;
29import java.util.Collection;
30import java.util.Locale;
31
32/**
33 * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys.
34 */
35public abstract class KeyProperties {
36    private KeyProperties() {}
37
38    /**
39     * @hide
40     */
41    @Retention(RetentionPolicy.SOURCE)
42    @IntDef(flag = true,
43            value = {
44                PURPOSE_ENCRYPT,
45                PURPOSE_DECRYPT,
46                PURPOSE_SIGN,
47                PURPOSE_VERIFY,
48                })
49    public @interface PurposeEnum {}
50
51    /**
52     * Purpose of key: encryption.
53     */
54    public static final int PURPOSE_ENCRYPT = 1 << 0;
55
56    /**
57     * Purpose of key: decryption.
58     */
59    public static final int PURPOSE_DECRYPT = 1 << 1;
60
61    /**
62     * Purpose of key: signing or generating a Message Authentication Code (MAC).
63     */
64    public static final int PURPOSE_SIGN = 1 << 2;
65
66    /**
67     * Purpose of key: signature or Message Authentication Code (MAC) verification.
68     */
69    public static final int PURPOSE_VERIFY = 1 << 3;
70
71    /**
72     * @hide
73     */
74    public static abstract class Purpose {
75        private Purpose() {}
76
77        public static int toKeymaster(@PurposeEnum int purpose) {
78            switch (purpose) {
79                case PURPOSE_ENCRYPT:
80                    return KeymasterDefs.KM_PURPOSE_ENCRYPT;
81                case PURPOSE_DECRYPT:
82                    return KeymasterDefs.KM_PURPOSE_DECRYPT;
83                case PURPOSE_SIGN:
84                    return KeymasterDefs.KM_PURPOSE_SIGN;
85                case PURPOSE_VERIFY:
86                    return KeymasterDefs.KM_PURPOSE_VERIFY;
87                default:
88                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
89            }
90        }
91
92        public static @PurposeEnum int fromKeymaster(int purpose) {
93            switch (purpose) {
94                case KeymasterDefs.KM_PURPOSE_ENCRYPT:
95                    return PURPOSE_ENCRYPT;
96                case KeymasterDefs.KM_PURPOSE_DECRYPT:
97                    return PURPOSE_DECRYPT;
98                case KeymasterDefs.KM_PURPOSE_SIGN:
99                    return PURPOSE_SIGN;
100                case KeymasterDefs.KM_PURPOSE_VERIFY:
101                    return PURPOSE_VERIFY;
102                default:
103                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
104            }
105        }
106
107        @NonNull
108        public static int[] allToKeymaster(@PurposeEnum int purposes) {
109            int[] result = getSetFlags(purposes);
110            for (int i = 0; i < result.length; i++) {
111                result[i] = toKeymaster(result[i]);
112            }
113            return result;
114        }
115
116        public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
117            @PurposeEnum int result = 0;
118            for (int keymasterPurpose : purposes) {
119                result |= fromKeymaster(keymasterPurpose);
120            }
121            return result;
122        }
123    }
124
125    /**
126     * @hide
127     */
128    @Retention(RetentionPolicy.SOURCE)
129    @StringDef({
130        KEY_ALGORITHM_RSA,
131        KEY_ALGORITHM_EC,
132        KEY_ALGORITHM_AES,
133        KEY_ALGORITHM_HMAC_SHA1,
134        KEY_ALGORITHM_HMAC_SHA224,
135        KEY_ALGORITHM_HMAC_SHA256,
136        KEY_ALGORITHM_HMAC_SHA384,
137        KEY_ALGORITHM_HMAC_SHA512,
138        })
139    public @interface KeyAlgorithmEnum {}
140
141    /** Rivest Shamir Adleman (RSA) key. */
142    public static final String KEY_ALGORITHM_RSA = "RSA";
143
144    /** Elliptic Curve (EC) Cryptography key. */
145    public static final String KEY_ALGORITHM_EC = "EC";
146
147    /** Advanced Encryption Standard (AES) key. */
148    public static final String KEY_ALGORITHM_AES = "AES";
149
150    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
151    public static final String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";
152
153    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-224 as the hash. */
154    public static final String KEY_ALGORITHM_HMAC_SHA224 = "HmacSHA224";
155
156    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-256 as the hash. */
157    public static final String KEY_ALGORITHM_HMAC_SHA256 = "HmacSHA256";
158
159    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-384 as the hash. */
160    public static final String KEY_ALGORITHM_HMAC_SHA384 = "HmacSHA384";
161
162    /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-512 as the hash. */
163    public static final String KEY_ALGORITHM_HMAC_SHA512 = "HmacSHA512";
164
165    /**
166     * @hide
167     */
168    public static abstract class KeyAlgorithm {
169        private KeyAlgorithm() {}
170
171        public static int toKeymasterSecretKeyAlgorithm(
172                @NonNull @KeyAlgorithmEnum String algorithm) {
173            if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
174                return KeymasterDefs.KM_ALGORITHM_AES;
175            } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
176                return KeymasterDefs.KM_ALGORITHM_HMAC;
177            } else {
178                throw new IllegalArgumentException(
179                        "Unsupported secret key algorithm: " + algorithm);
180            }
181        }
182
183        @NonNull
184        public static @KeyAlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
185                int keymasterAlgorithm, int keymasterDigest) {
186            switch (keymasterAlgorithm) {
187                case KeymasterDefs.KM_ALGORITHM_AES:
188                    if (keymasterDigest != -1) {
189                        throw new IllegalArgumentException("Digest not supported for AES key: "
190                                + Digest.fromKeymaster(keymasterDigest));
191                    }
192                    return KEY_ALGORITHM_AES;
193                case KeymasterDefs.KM_ALGORITHM_HMAC:
194                    switch (keymasterDigest) {
195                        case KeymasterDefs.KM_DIGEST_SHA1:
196                            return KEY_ALGORITHM_HMAC_SHA1;
197                        case KeymasterDefs.KM_DIGEST_SHA_2_224:
198                            return KEY_ALGORITHM_HMAC_SHA224;
199                        case KeymasterDefs.KM_DIGEST_SHA_2_256:
200                            return KEY_ALGORITHM_HMAC_SHA256;
201                        case KeymasterDefs.KM_DIGEST_SHA_2_384:
202                            return KEY_ALGORITHM_HMAC_SHA384;
203                        case KeymasterDefs.KM_DIGEST_SHA_2_512:
204                            return KEY_ALGORITHM_HMAC_SHA512;
205                        default:
206                            throw new IllegalArgumentException("Unsupported HMAC digest: "
207                                    + Digest.fromKeymaster(keymasterDigest));
208                    }
209                default:
210                    throw new IllegalArgumentException(
211                            "Unsupported key algorithm: " + keymasterAlgorithm);
212            }
213        }
214
215        /**
216         * @hide
217         *
218         * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
219         */
220        public static int toKeymasterDigest(@NonNull @KeyAlgorithmEnum String algorithm) {
221            String algorithmUpper = algorithm.toUpperCase(Locale.US);
222            if (algorithmUpper.startsWith("HMAC")) {
223                String digestUpper = algorithmUpper.substring("HMAC".length());
224                switch (digestUpper) {
225                    case "SHA1":
226                        return KeymasterDefs.KM_DIGEST_SHA1;
227                    case "SHA224":
228                        return KeymasterDefs.KM_DIGEST_SHA_2_224;
229                    case "SHA256":
230                        return KeymasterDefs.KM_DIGEST_SHA_2_256;
231                    case "SHA384":
232                        return KeymasterDefs.KM_DIGEST_SHA_2_384;
233                    case "SHA512":
234                        return KeymasterDefs.KM_DIGEST_SHA_2_512;
235                    default:
236                        throw new IllegalArgumentException(
237                                "Unsupported HMAC digest: " + digestUpper);
238                }
239            } else {
240                return -1;
241            }
242        }
243    }
244
245    /**
246     * @hide
247     */
248    @Retention(RetentionPolicy.SOURCE)
249    @StringDef({
250        BLOCK_MODE_ECB,
251        BLOCK_MODE_CBC,
252        BLOCK_MODE_CTR,
253        BLOCK_MODE_GCM,
254        })
255    public @interface BlockModeEnum {}
256
257    /** Electronic Codebook (ECB) block mode. */
258    public static final String BLOCK_MODE_ECB = "ECB";
259
260    /** Cipher Block Chaining (CBC) block mode. */
261    public static final String BLOCK_MODE_CBC = "CBC";
262
263    /** Counter (CTR) block mode. */
264    public static final String BLOCK_MODE_CTR = "CTR";
265
266    /** Galois/Counter Mode (GCM) block mode. */
267    public static final String BLOCK_MODE_GCM = "GCM";
268
269    /**
270     * @hide
271     */
272    public static abstract class BlockMode {
273        private BlockMode() {}
274
275        public static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
276            if (BLOCK_MODE_ECB.equalsIgnoreCase(blockMode)) {
277                return KeymasterDefs.KM_MODE_ECB;
278            } else if (BLOCK_MODE_CBC.equalsIgnoreCase(blockMode)) {
279                return KeymasterDefs.KM_MODE_CBC;
280            } else if (BLOCK_MODE_CTR.equalsIgnoreCase(blockMode)) {
281                return KeymasterDefs.KM_MODE_CTR;
282            } else if (BLOCK_MODE_GCM.equalsIgnoreCase(blockMode)) {
283                return KeymasterDefs.KM_MODE_GCM;
284            } else {
285                throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
286            }
287        }
288
289        @NonNull
290        public static @BlockModeEnum String fromKeymaster(int blockMode) {
291            switch (blockMode) {
292                case KeymasterDefs.KM_MODE_ECB:
293                    return BLOCK_MODE_ECB;
294                case KeymasterDefs.KM_MODE_CBC:
295                    return BLOCK_MODE_CBC;
296                case KeymasterDefs.KM_MODE_CTR:
297                    return BLOCK_MODE_CTR;
298                case KeymasterDefs.KM_MODE_GCM:
299                    return BLOCK_MODE_GCM;
300                default:
301                    throw new IllegalArgumentException("Unsupported block mode: " + blockMode);
302            }
303        }
304
305        @NonNull
306        public static @BlockModeEnum String[] allFromKeymaster(
307                @NonNull Collection<Integer> blockModes) {
308            if ((blockModes == null) || (blockModes.isEmpty())) {
309                return EmptyArray.STRING;
310            }
311            @BlockModeEnum String[] result = new String[blockModes.size()];
312            int offset = 0;
313            for (int blockMode : blockModes) {
314                result[offset] = fromKeymaster(blockMode);
315                offset++;
316            }
317            return result;
318        }
319
320        public static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
321            if ((blockModes == null) || (blockModes.length == 0)) {
322                return EmptyArray.INT;
323            }
324            int[] result = new int[blockModes.length];
325            for (int i = 0; i < blockModes.length; i++) {
326                result[i] = toKeymaster(blockModes[i]);
327            }
328            return result;
329        }
330    }
331
332    /**
333     * @hide
334     */
335    @Retention(RetentionPolicy.SOURCE)
336    @StringDef({
337        ENCRYPTION_PADDING_NONE,
338        ENCRYPTION_PADDING_PKCS7,
339        ENCRYPTION_PADDING_RSA_PKCS1,
340        ENCRYPTION_PADDING_RSA_OAEP,
341        })
342    public @interface EncryptionPaddingEnum {}
343
344    /**
345     * No encryption padding.
346     */
347    public static final String ENCRYPTION_PADDING_NONE = "NoPadding";
348
349    /**
350     * PKCS#7 encryption padding scheme.
351     */
352    public static final String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
353
354    /**
355     * RSA PKCS#1 v1.5 padding scheme for encryption.
356     */
357    public static final String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
358
359    /**
360     * RSA Optimal Asymmetric Encryption Padding (OAEP) scheme.
361     */
362    public static final String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
363
364    /**
365     * @hide
366     */
367    public static abstract class EncryptionPadding {
368        private EncryptionPadding() {}
369
370        public static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
371            if (ENCRYPTION_PADDING_NONE.equalsIgnoreCase(padding)) {
372                return KeymasterDefs.KM_PAD_NONE;
373            } else if (ENCRYPTION_PADDING_PKCS7.equalsIgnoreCase(padding)) {
374                return KeymasterDefs.KM_PAD_PKCS7;
375            } else if (ENCRYPTION_PADDING_RSA_PKCS1.equalsIgnoreCase(padding)) {
376                return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
377            } else if (ENCRYPTION_PADDING_RSA_OAEP.equalsIgnoreCase(padding)) {
378                return KeymasterDefs.KM_PAD_RSA_OAEP;
379            } else {
380                throw new IllegalArgumentException(
381                        "Unsupported encryption padding scheme: " + padding);
382            }
383        }
384
385        @NonNull
386        public static @EncryptionPaddingEnum String fromKeymaster(int padding) {
387            switch (padding) {
388                case KeymasterDefs.KM_PAD_NONE:
389                    return ENCRYPTION_PADDING_NONE;
390                case KeymasterDefs.KM_PAD_PKCS7:
391                    return ENCRYPTION_PADDING_PKCS7;
392                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
393                    return ENCRYPTION_PADDING_RSA_PKCS1;
394                case KeymasterDefs.KM_PAD_RSA_OAEP:
395                    return ENCRYPTION_PADDING_RSA_OAEP;
396                default:
397                    throw new IllegalArgumentException(
398                            "Unsupported encryption padding: " + padding);
399            }
400        }
401
402        @NonNull
403        public static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
404            if ((paddings == null) || (paddings.length == 0)) {
405                return EmptyArray.INT;
406            }
407            int[] result = new int[paddings.length];
408            for (int i = 0; i < paddings.length; i++) {
409                result[i] = toKeymaster(paddings[i]);
410            }
411            return result;
412        }
413    }
414
415    /**
416     * @hide
417     */
418    @Retention(RetentionPolicy.SOURCE)
419    @StringDef({
420        SIGNATURE_PADDING_RSA_PKCS1,
421        SIGNATURE_PADDING_RSA_PSS,
422        })
423    public @interface SignaturePaddingEnum {}
424
425    /**
426     * RSA PKCS#1 v1.5 padding for signatures.
427     */
428    public static final String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
429
430    /**
431     * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
432     */
433    public static final String SIGNATURE_PADDING_RSA_PSS = "PSS";
434
435    static abstract class SignaturePadding {
436        private SignaturePadding() {}
437
438        static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
439            switch (padding.toUpperCase(Locale.US)) {
440                case SIGNATURE_PADDING_RSA_PKCS1:
441                    return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
442                case SIGNATURE_PADDING_RSA_PSS:
443                    return KeymasterDefs.KM_PAD_RSA_PSS;
444                default:
445                    throw new IllegalArgumentException(
446                            "Unsupported signature padding scheme: " + padding);
447            }
448        }
449
450        @NonNull
451        static @SignaturePaddingEnum String fromKeymaster(int padding) {
452            switch (padding) {
453                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
454                    return SIGNATURE_PADDING_RSA_PKCS1;
455                case KeymasterDefs.KM_PAD_RSA_PSS:
456                    return SIGNATURE_PADDING_RSA_PSS;
457                default:
458                    throw new IllegalArgumentException("Unsupported signature padding: " + padding);
459            }
460        }
461
462        @NonNull
463        static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) {
464            if ((paddings == null) || (paddings.length == 0)) {
465                return EmptyArray.INT;
466            }
467            int[] result = new int[paddings.length];
468            for (int i = 0; i < paddings.length; i++) {
469                result[i] = toKeymaster(paddings[i]);
470            }
471            return result;
472        }
473    }
474
475    /**
476     * @hide
477     */
478    @Retention(RetentionPolicy.SOURCE)
479    @StringDef({
480        DIGEST_NONE,
481        DIGEST_MD5,
482        DIGEST_SHA1,
483        DIGEST_SHA224,
484        DIGEST_SHA256,
485        DIGEST_SHA384,
486        DIGEST_SHA512,
487        })
488    public @interface DigestEnum {}
489
490    /**
491     * No digest: sign/authenticate the raw message.
492     */
493    public static final String DIGEST_NONE = "NONE";
494
495    /**
496     * MD5 digest.
497     */
498    public static final String DIGEST_MD5 = "MD5";
499
500    /**
501     * SHA-1 digest.
502     */
503    public static final String DIGEST_SHA1 = "SHA-1";
504
505    /**
506     * SHA-2 224 (aka SHA-224) digest.
507     */
508    public static final String DIGEST_SHA224 = "SHA-224";
509
510    /**
511     * SHA-2 256 (aka SHA-256) digest.
512     */
513    public static final String DIGEST_SHA256 = "SHA-256";
514
515    /**
516     * SHA-2 384 (aka SHA-384) digest.
517     */
518    public static final String DIGEST_SHA384 = "SHA-384";
519
520    /**
521     * SHA-2 512 (aka SHA-512) digest.
522     */
523    public static final String DIGEST_SHA512 = "SHA-512";
524
525    /**
526     * @hide
527     */
528    public static abstract class Digest {
529        private Digest() {}
530
531        public static int toKeymaster(@NonNull @DigestEnum String digest) {
532            switch (digest.toUpperCase(Locale.US)) {
533                case DIGEST_SHA1:
534                    return KeymasterDefs.KM_DIGEST_SHA1;
535                case DIGEST_SHA224:
536                    return KeymasterDefs.KM_DIGEST_SHA_2_224;
537                case DIGEST_SHA256:
538                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
539                case DIGEST_SHA384:
540                    return KeymasterDefs.KM_DIGEST_SHA_2_384;
541                case DIGEST_SHA512:
542                    return KeymasterDefs.KM_DIGEST_SHA_2_512;
543                case DIGEST_NONE:
544                    return KeymasterDefs.KM_DIGEST_NONE;
545                case DIGEST_MD5:
546                    return KeymasterDefs.KM_DIGEST_MD5;
547                default:
548                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
549            }
550        }
551
552        @NonNull
553        public static @DigestEnum String fromKeymaster(int digest) {
554            switch (digest) {
555                case KeymasterDefs.KM_DIGEST_NONE:
556                    return DIGEST_NONE;
557                case KeymasterDefs.KM_DIGEST_MD5:
558                    return DIGEST_MD5;
559                case KeymasterDefs.KM_DIGEST_SHA1:
560                    return DIGEST_SHA1;
561                case KeymasterDefs.KM_DIGEST_SHA_2_224:
562                    return DIGEST_SHA224;
563                case KeymasterDefs.KM_DIGEST_SHA_2_256:
564                    return DIGEST_SHA256;
565                case KeymasterDefs.KM_DIGEST_SHA_2_384:
566                    return DIGEST_SHA384;
567                case KeymasterDefs.KM_DIGEST_SHA_2_512:
568                    return DIGEST_SHA512;
569                default:
570                    throw new IllegalArgumentException("Unsupported digest algorithm: " + digest);
571            }
572        }
573
574        @NonNull
575        public static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
576            if (digests.isEmpty()) {
577                return EmptyArray.STRING;
578            }
579            String[] result = new String[digests.size()];
580            int offset = 0;
581            for (int digest : digests) {
582                result[offset] = fromKeymaster(digest);
583                offset++;
584            }
585            return result;
586        }
587
588        @NonNull
589        public static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
590            if ((digests == null) || (digests.length == 0)) {
591                return EmptyArray.INT;
592            }
593            int[] result = new int[digests.length];
594            int offset = 0;
595            for (@DigestEnum String digest : digests) {
596                result[offset] = toKeymaster(digest);
597                offset++;
598            }
599            return result;
600        }
601    }
602
603    /**
604     * @hide
605     */
606    @Retention(RetentionPolicy.SOURCE)
607    @IntDef({
608        ORIGIN_GENERATED,
609        ORIGIN_IMPORTED,
610        ORIGIN_UNKNOWN,
611        })
612    public @interface OriginEnum {}
613
614    /** Key was generated inside AndroidKeyStore. */
615    public static final int ORIGIN_GENERATED = 1 << 0;
616
617    /** Key was imported into AndroidKeyStore. */
618    public static final int ORIGIN_IMPORTED = 1 << 1;
619
620    /**
621     * Origin of the key is unknown. This can occur only for keys backed by an old TEE-backed
622     * implementation which does not record origin information.
623     */
624    public static final int ORIGIN_UNKNOWN = 1 << 2;
625
626    /**
627     * @hide
628     */
629    public static abstract class Origin {
630        private Origin() {}
631
632        public static @OriginEnum int fromKeymaster(int origin) {
633            switch (origin) {
634                case KeymasterDefs.KM_ORIGIN_GENERATED:
635                    return ORIGIN_GENERATED;
636                case KeymasterDefs.KM_ORIGIN_IMPORTED:
637                    return ORIGIN_IMPORTED;
638                case KeymasterDefs.KM_ORIGIN_UNKNOWN:
639                    return ORIGIN_UNKNOWN;
640                default:
641                    throw new IllegalArgumentException("Unknown origin: " + origin);
642            }
643        }
644    }
645
646    private static int[] getSetFlags(int flags) {
647        if (flags == 0) {
648            return EmptyArray.INT;
649        }
650        int result[] = new int[getSetBitCount(flags)];
651        int resultOffset = 0;
652        int flag = 1;
653        while (flags != 0) {
654            if ((flags & 1) != 0) {
655                result[resultOffset] = flag;
656                resultOffset++;
657            }
658            flags >>>= 1;
659            flag <<= 1;
660        }
661        return result;
662    }
663
664    private static int getSetBitCount(int value) {
665        if (value == 0) {
666            return 0;
667        }
668        int result = 0;
669        while (value != 0) {
670            if ((value & 1) != 0) {
671                result++;
672            }
673            value >>>= 1;
674        }
675        return result;
676    }
677}
678