1 // Copyright 2021 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 //////////////////////////////////////////////////////////////////////////////// 16 17 package com.google.crypto.tink.internal; 18 19 import com.google.crypto.tink.util.Bytes; 20 import java.nio.charset.Charset; 21 import java.security.GeneralSecurityException; 22 import java.security.SecureRandom; 23 import java.util.Objects; 24 import javax.annotation.Nullable; 25 26 /** Helper functions used throughout Tink, for Tink internal use only. */ 27 public final class Util { 28 /** Android 18-compatible alternative to {@link java.nio.charset.StandardCharsets#UTF_8}. */ 29 public static final Charset UTF_8 = Charset.forName("UTF-8"); 30 31 /** Returns a positive random int which can be used as a key ID in a keyset. */ randKeyId()32 public static int randKeyId() { 33 SecureRandom secureRandom = new SecureRandom(); 34 byte[] rand = new byte[4]; 35 int result = 0; 36 while (result == 0) { 37 secureRandom.nextBytes(rand); 38 // TODO(b/148124847): Other languages create key_ids with the MSB set, so we should here too. 39 result = 40 ((rand[0] & 0x7f) << 24) 41 | ((rand[1] & 0xff) << 16) 42 | ((rand[2] & 0xff) << 8) 43 | (rand[3] & 0xff); 44 } 45 return result; 46 } 47 toByteFromPrintableAscii(char c)48 private static final byte toByteFromPrintableAscii(char c) { 49 if (c < '!' || c > '~') { 50 throw new TinkBugException("Not a printable ASCII character: " + c); 51 } 52 return (byte) c; 53 } 54 checkedToByteFromPrintableAscii(char c)55 private static final byte checkedToByteFromPrintableAscii(char c) 56 throws GeneralSecurityException { 57 if (c < '!' || c > '~') { 58 throw new GeneralSecurityException("Not a printable ASCII character: " + c); 59 } 60 return (byte) c; 61 } 62 63 /** 64 * Converts a string {@code s} to a corresponding {@link Bytes} object. 65 * 66 * <p>The string must contain only printable ASCII characters; calling it in any other way is a 67 * considered a bug in Tink. Spaces are not allowed. 68 * 69 * @throws TinkBugException if s contains a character which is not a printable ASCII character or 70 * a space. 71 */ toBytesFromPrintableAscii(String s)72 public static final Bytes toBytesFromPrintableAscii(String s) { 73 byte[] result = new byte[s.length()]; 74 for (int i = 0; i < s.length(); ++i) { 75 result[i] = toByteFromPrintableAscii(s.charAt(i)); 76 } 77 return Bytes.copyFrom(result); 78 } 79 80 /** 81 * Converts a string {@code s} to a corresponding {@link Bytes} object. 82 * 83 * @throws GeneralSecurityException if s contains a character which is not a printable ASCII 84 * character or a space. 85 */ checkedToBytesFromPrintableAscii(String s)86 public static final Bytes checkedToBytesFromPrintableAscii(String s) 87 throws GeneralSecurityException { 88 byte[] result = new byte[s.length()]; 89 for (int i = 0; i < s.length(); ++i) { 90 result[i] = checkedToByteFromPrintableAscii(s.charAt(i)); 91 } 92 return Bytes.copyFrom(result); 93 } 94 95 /** 96 * Best-effort checks that this is Android. 97 * 98 * <p>Note: this is more tricky than it seems. For example, using reflection to see if 99 * android.os.Build.SDK_INT exists might fail because proguard might break the 100 * reflection part. Using build dispatching can also fail if there are issues in the build graph 101 * (see cl/510374081). 102 * 103 * @return true if running on Android. 104 */ isAndroid()105 public static boolean isAndroid() { 106 // https://developer.android.com/reference/java/lang/System#getProperties%28%29 107 return Objects.equals(System.getProperty("java.vendor"), "The Android Project"); 108 } 109 110 /** Returns the current Android API level as integer or null if we do not run on Android. */ 111 @Nullable getAndroidApiLevel()112 public static Integer getAndroidApiLevel() { 113 if (!isAndroid()) { 114 return null; 115 } 116 return BuildDispatchedCode.getApiLevel(); 117 } 118 Util()119 private Util() {} 120 } 121