Merge "KeyStore: Fix key name decoding" am: 41bf9b04e4
am: fd9c8781e8

Change-Id: I6bbc85c470151521537578980306a2a510aa10b6
diff --git a/keystore/Android.bp b/keystore/Android.bp
index 295d605..366f591 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -234,6 +234,7 @@
     srcs: [
         ":IKeyAttestationApplicationIdProvider.aidl",
         "auth_token_table.cpp",
+        "blob.cpp",
         "keystore_attestation_id.cpp",
         "KeyAttestationApplicationId.cpp",
         "KeyAttestationPackageInfo.cpp",
diff --git a/keystore/blob.cpp b/keystore/blob.cpp
index f08e08d..f887e80 100644
--- a/keystore/blob.cpp
+++ b/keystore/blob.cpp
@@ -559,15 +559,23 @@
  * [0-o]. Therefore in the worst case the length of a key gets doubled. Note
  * that Base64 cannot be used here due to the need of prefix match on keys. */
 
-static std::string encodeKeyName(const std::string& keyName) {
+std::string encodeKeyName(const std::string& keyName) {
     std::string encodedName;
     encodedName.reserve(keyName.size() * 2);
     auto in = keyName.begin();
     while (in != keyName.end()) {
+        // Input character needs to be encoded.
         if (*in < '0' || *in > '~') {
+            // Encode the two most-significant bits of the input char in the first
+            // output character, by counting up from 43 ('+').
             encodedName.append(1, '+' + (uint8_t(*in) >> 6));
+            // Encode the six least-significant bits of the input char in the second
+            // output character, by counting up from 48 ('0').
+            // This is safe because the maximum value is 112, which is the
+            // character 'p'.
             encodedName.append(1, '0' + (*in & 0x3F));
         } else {
+            // No need to encode input char - append as-is.
             encodedName.append(1, *in);
         }
         ++in;
@@ -575,7 +583,7 @@
     return encodedName;
 }
 
-static std::string decodeKeyName(const std::string& encodedName) {
+std::string decodeKeyName(const std::string& encodedName) {
     std::string decodedName;
     decodedName.reserve(encodedName.size());
     auto in = encodedName.begin();
@@ -583,12 +591,19 @@
     char c;
     while (in != encodedName.end()) {
         if (multichar) {
+            // Second part of a multi-character encoding. Turn off the multichar
+            // flag and set the six least-significant bits of c to the value originally
+            // encoded by counting up from '0'.
             multichar = false;
-            decodedName.append(1, c | *in);
+            decodedName.append(1, c | (uint8_t(*in) - '0'));
         } else if (*in >= '+' && *in <= '.') {
+            // First part of a multi-character encoding. Set the multichar flag
+            // and set the two most-significant bits of c to be the two bits originally
+            // encoded by counting up from '+'.
             multichar = true;
             c = (*in - '+') << 6;
         } else {
+            // Regular character, append as-is.
             decodedName.append(1, *in);
         }
         ++in;
diff --git a/keystore/blob.h b/keystore/blob.h
index a7f9fd0..92e4514 100644
--- a/keystore/blob.h
+++ b/keystore/blob.h
@@ -272,4 +272,8 @@
     inline const KeyBlobEntry* operator->() const { return entry_; }
 };
 
+// Visible for testing
+std::string encodeKeyName(const std::string& keyName);
+std::string decodeKeyName(const std::string& encodedName);
+
 #endif  // KEYSTORE_BLOB_H_
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index 103fa0e..1ce1210 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -11,6 +11,7 @@
         "aaid_truncation_test.cpp",
         "auth_token_table_test.cpp",
         "auth_token_formatting_test.cpp",
+        "blob_test.cpp",
         "confirmationui_rate_limiting_test.cpp",
         "gtest_main.cpp",
     ],
diff --git a/keystore/tests/blob_test.cpp b/keystore/tests/blob_test.cpp
new file mode 100644
index 0000000..485bd88
--- /dev/null
+++ b/keystore/tests/blob_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <utils/String16.h>
+
+#include "../blob.h"
+
+namespace keystore {
+
+namespace test {
+
+namespace {
+
+constexpr const char* kNameToEncode = "some key name !\\ %#|\"";
+
+}  // namespace
+
+TEST(BlobTest, nameEncodingAndDecodingTest) {
+    std::string toEncode(kNameToEncode);
+    std::string decoded(decodeKeyName(encodeKeyName(toEncode)));
+
+    ASSERT_EQ(toEncode, decoded);
+}
+
+}  // namespace test
+}  // namespace keystore