Coder Social home page Coder Social logo

Comments (3)

yugui avatar yugui commented on August 11, 2024

Minimal Reproducible Example

This is the minimal reproducible example:

Encoder.cs:

using System;
using System.Text;
using System.Text.RegularExpressions;

class Encoder {
  static void Main(string[] args) {
    // c.f. https://github.com/bitwarden/mobile/blob/8a43bb465515d3a775e60eb083cf88ef0e3e70c8/src/Core/Pages/Accounts/TwoFactorPageViewModel.cs#L343
    // c.f. https://github.com/bitwarden/mobile/blob/8a43bb465515d3a775e60eb083cf88ef0e3e70c8/src/Core/Resources/Localization/AppResources.ja.resx#L2128
    var message = "WebAuthn の認証";
    var json = $"{{\"btnText\": \"{message}\"}}";

    // c.f. https://github.com/bitwarden/mobile/blob/main/src/Core/Utilities/AppHelpers.cs#L523
    string EncodeMultibyte(Match match)
     {
      return Convert.ToChar(Convert.ToUInt32($"0x{match.Groups[1].Value}", 16)).ToString();
     }

    var escaped = Uri.EscapeDataString(json);
    Console.WriteLine(escaped);
    var multiByteEscaped = Regex.Replace(escaped, "%([0-9A-F]{2})", EncodeMultibyte);
    var data = Convert.ToBase64String(Encoding.UTF8.GetBytes(multiByteEscaped));

    Console.WriteLine(data);
  }
}

Decoder.js:

// c.f. https://github.com/bitwarden/clients/blob/48de33fc7a360e0a23df3351b3ad63d1c5579aac/apps/web/src/connectors/common.ts#L18
function b64Decode(str) {
  return decodeURIComponent(
    Array.prototype.map
      .call(atob(str), (c) => {
        return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
      })
     .join(""),
  );
}

// c.f. https://github.com/bitwarden/clients/blob/48de33fc7a360e0a23df3351b3ad63d1c5579aac/apps/web/src/connectors/webauthn.ts#L88
function decode(data) {
  const dataObj = JSON.parse(b64Decode(data));
  console.log(dataObj.btnText);
}

decode(process.argv[2]);

The next command would generate the same mojibake text as the one that WebAuth Mobile Connector displayed:

$ node Decoder.js $(mono Encoder.exe)
WebAuthn ã�®èª�è

Fix

The next version of the Encoder would fix the issue, and make the message consumer correctly recover the original message value.

using System;
using System.Text;
using System.Text.RegularExpressions;

class Encoder {
  static void Main(string[] args) {
    // c.f. https://github.com/bitwarden/mobile/blob/8a43bb465515d3a775e60eb083cf88ef0e3e70c8/src/Core/Pages/Accounts/TwoFactorPageViewModel.cs#L343
    // c.f. https://github.com/bitwarden/mobile/blob/8a43bb465515d3a775e60eb083cf88ef0e3e70c8/src/Core/Resources/Localization/AppResources.ja.resx#L2128
    var message = "WebAuthn の認証";
    var json = $"{{\"btnText\": \"{message}\"}}";
    var data = Convert.ToBase64String(Encoding.UTF8.GetBytes(json));

    Console.WriteLine(data);
  }
}
$ node Decoder.js $(mono Encoder.exe)
WebAuthn の認証

Analysis

Let's take a look at the first multibyte character in the original message, "の" (U+306E), for example.

  1. Encoding into JSON preserves the character because ECMA-404 does not require character escapes in strings values except for " (double quotation) or \ (backslash).
  2. Uri.EscapeDataString internally calls System.UriHelper.EscapeStringToBuilder and it encodes the character into its UTF-8 byte sequence representation, and then individual bytes in the sequence are encoded into the form of "%xx".
    • U+306E is represented as 0xE3, 0x81, 0xAE in UTF-8
    • It is encoded into "%E3%81%AE"
  3. The invocation of Regex.Replace replaces the individual occurrences of "%xx" form with EncodeMultibyte
    • Convert.ToUInt32 in EncodeMultibyte converts the regex captures of "E3", "81", and "AE" into 0xE3u, 0x81u, and 0xAEu, respectively
    • Convert.ToChar converts individual integer values into their corresponding unicode code points.
      0xE3u, 0x81u, and 0xAEu are converted into the following characters, respectively:
      • ã (U+00E3, LATIN SMALL LETTER A WITH TILDE)
      • a non-printable control character (U+0081)
      • ® (U+00AE, REGISTERED SIGN)
  4. Encoding.UTF8.GetBytes returns the UTF-8 byte sequence representation of the given string.
    • The characters ã, , and ® are converted into their corresponding byte sequences as the followings:
      • ã (U+00E3) --> 0xC3, 0xA3
      • U+0081 --> 0xC2, 0x81
      • ® (U+00AE) --> 0xC2", 0xAE
  5. Convert.ToBase64String converts the 6 bytes into "w6PCgcKu".

The consumer-side implementation reverses just the step (5), (4), and (1). Therefore, we get ã�® (U+00E3, U+0031, U+00AE) instead of "の" (U306E).

Justification of the proposed fix

If I understand correctly, the intention of this conversion in AppHelpers::EncodeDataParameter is to keep the multibyte characters url-safe.

Therefore, just WebUtility.UrlEncode would be sufficient. It internally converts the given string into its UTF-8 byte sequence representation, and individual bytes into a url-safe characters.

In practice, Base64 encoding is also necessary for consistency with the consumer-side.

In any way, the earlier steps of (2), and (3) are not necessary at all. Both of WebUtility.UrlEncode and Convert.ToBase64String(Encoding.UTF8.GetBytes(str)) are safe to multibyte characters, and they are what the consumer-side implementation reverses.

from mobile.

daniellbw avatar daniellbw commented on August 11, 2024

Hi there,

This issue has been escalated for further investigation. If you have more information that can help us, please add it below.

Thanks!

from mobile.

yugui avatar yugui commented on August 11, 2024

Thank you for your response, @daniellbw.

Just in case, I have already prepared a fix for this issue in #3346

from mobile.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.