Comments (3)
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.
- Encoding into JSON preserves the character because ECMA-404 does not require character escapes in strings values except for
"
(double quotation) or\
(backslash). Uri.EscapeDataString
internally callsSystem.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"
- U+306E is represented as
- The invocation of
Regex.Replace
replaces the individual occurrences of "%xx" form withEncodeMultibyte
Convert.ToUInt32
inEncodeMultibyte
converts the regex captures of "E3", "81", and "AE" into0xE3u
,0x81u
, and0xAEu
, respectivelyConvert.ToChar
converts individual integer values into their corresponding unicode code points.
0xE3u
,0x81u
, and0xAEu
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)
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
- The characters
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.
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.
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)
- Android beta app Screenshot issues HOT 1
- Bitwarden’s F-Droid Repository Webpage is Missing. HOT 2
- Android beta login incorrect HOT 1
- iOS App: Creating Secure Note Text Field with 2 or more consecutive hyphens crashes the app HOT 2
- When switching apps in dark mode Android, the app is shown as completely white HOT 1
- "App doesn't respond" - "Close app / or wait" HOT 3
- Android beta login with 2FA. HOT 2
- Dependency Dashboard
- Android beta - email field is case sensitive on login HOT 2
- If Bitwarden is left in the background whilst viewing a vault item, that vault item would be displayed twice after launching Bitwarden anew.
- App crashes when trying to select and delete notes of vault item HOT 3
- non-discoverable passkey authenticatation not supported HOT 2
- Not support web browser com.tencent.mtt HOT 1
- Opening attached image should open internally HOT 1
- Bitwarden 2024.7.0 version 11080 HOT 2
- I cannot login to Bitwarden for a few days. It is showing the error "Exception message: Connection failure". HOT 12
- Switching between Bitwarden user accounts (enterprise to personal) , cant see items in my vault HOT 1
- Special characters excluded from view HOT 1
- Basic search only works on first URI for a given login item HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from mobile.