Allow CBOR maps to use integer keys

Currently, CBOR maps only permit keys to be UTF-8 strings. Add support
for unsigned integer keys. Support for negative integer type keys will
be integrated as CL 777807 is lands.

Bug: 786218
Change-Id: Id28864a9c62d0b0d93e71a663b6e8b2b84b2de95
Reviewed-on: https://chromium-review.googlesource.com/781207
Commit-Queue: Jun Choi <[email protected]>
Reviewed-by: Chris Palmer <[email protected]>
Reviewed-by: Balazs Engedy <[email protected]>
Reviewed-by: Jeffrey Yasskin <[email protected]>
Cr-Commit-Position: refs/heads/master@{#524223}
diff --git a/content/browser/webauth/cbor/cbor_values.h b/content/browser/webauth/cbor/cbor_values.h
index 83bbd49..bea7b38c 100644
--- a/content/browser/webauth/cbor/cbor_values.h
+++ b/content/browser/webauth/cbor/cbor_values.h
@@ -29,8 +29,7 @@
     //
     // The sort order defined in CTAP is:
     //   • If the major types are different, the one with the lower value in
-    //     numerical order sorts earlier. (Moot in this code because all keys
-    //     are strings.)
+    //     numerical order sorts earlier.
     //   • If two keys have different lengths, the shorter one sorts earlier.
     //   • If two keys have the same length, the one with the lower value in
     //     (byte-wise) lexical order sorts earlier.
@@ -38,17 +37,32 @@
     // See section 6 of https://fidoalliance.org/specs/fido-v2.0-rd-20170927/
     // fido-client-to-authenticator-protocol-v2.0-rd-20170927.html.
     //
-    // The sort order defined in
-    // https://tools.ietf.org/html/rfc7049#section-3.9 is similar to the CTAP
-    // order implemented here, but it sorts purely by serialised key and
-    // doesn't specify that major types are compared first. Thus the shortest
-    // key sorts first by the RFC rules (irrespective of the major type), but
-    // may not by CTAP rules.
-    bool operator()(const base::StringPiece& a,
-                    const base::StringPiece& b) const {
-      const size_t a_size = a.size();
-      const size_t b_size = b.size();
-      return std::tie(a_size, a) < std::tie(b_size, b);
+    // THE CTAP SORT ORDER IMPLEMENTED HERE DIFFERS FROM THE CANONICAL CBOR
+    // ORDER defined in https://tools.ietf.org/html/rfc7049#section-3.9, in that
+    // the latter sorts purely by serialised key and doesn't specify that major
+    // types are compared first. Thus the shortest key sorts first by the RFC
+    // rules (irrespective of the major type), but may not by CTAP rules.
+    bool operator()(const CBORValue& a, const CBORValue& b) const {
+      DCHECK((a.is_unsigned() || a.is_string()) &&
+             (b.is_unsigned() || b.is_string()));
+      if (a.type() != b.type())
+        return a.type() < b.type();
+      switch (a.type()) {
+        case Type::UNSIGNED:
+          return a.GetUnsigned() < b.GetUnsigned();
+        case Type::STRING: {
+          const auto& a_str = a.GetString();
+          const size_t a_length = a_str.size();
+          const auto& b_str = b.GetString();
+          const size_t b_length = b_str.size();
+          return std::tie(a_length, a_str) < std::tie(b_length, b_str);
+        }
+        default:
+          break;
+      }
+
+      NOTREACHED();
+      return false;
     }
 
     using is_transparent = void;
@@ -56,7 +70,7 @@
 
   using BinaryValue = std::vector<uint8_t>;
   using ArrayValue = std::vector<CBORValue>;
-  using MapValue = base::flat_map<std::string, CBORValue, CTAPLess>;
+  using MapValue = base::flat_map<CBORValue, CBORValue, CTAPLess>;
 
   enum class Type {
     UNSIGNED = 0,
@@ -107,7 +121,7 @@
   bool is_map() const { return type() == Type::MAP; }
 
   // These will all fatally assert if the type doesn't match.
-  uint64_t GetUnsigned() const;
+  const uint64_t& GetUnsigned() const;
   const BinaryValue& GetBytestring() const;
   // Returned string may contain NUL characters.
   const std::string& GetString() const;