fix: Allow unpadded base64 decoding

This commit is contained in:
Lanna Michalke 2022-01-11 09:02:34 +00:00 committed by Krille Fear
parent d31a594068
commit cab03aa73b
7 changed files with 78 additions and 17 deletions

View File

@ -16,9 +16,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import 'dart:convert';
import 'dart:typed_data';
import 'package:matrix/encryption/utils/base64_unpadded.dart';
import 'package:olm/olm.dart' as olm;
import '../matrix.dart';
@ -33,7 +33,7 @@ class CrossSigning {
(String secret) async {
final keyObj = olm.PkSigning();
try {
return keyObj.init_with_seed(base64.decode(secret)) ==
return keyObj.init_with_seed(base64decodeUnpadded(secret)) ==
client.userDeviceKeys[client.userID]!.selfSigningKey!.ed25519Key;
} catch (_) {
return false;
@ -45,7 +45,7 @@ class CrossSigning {
(String secret) async {
final keyObj = olm.PkSigning();
try {
return keyObj.init_with_seed(base64.decode(secret)) ==
return keyObj.init_with_seed(base64decodeUnpadded(secret)) ==
client.userDeviceKeys[client.userID]!.userSigningKey!.ed25519Key;
} catch (_) {
return false;
@ -87,8 +87,8 @@ class CrossSigning {
);
await handle.maybeCacheAll();
}
final masterPrivateKey =
base64.decode(await handle.getStored(EventTypes.CrossSigningMasterKey));
final masterPrivateKey = base64decodeUnpadded(
await handle.getStored(EventTypes.CrossSigningMasterKey));
final keyObj = olm.PkSigning();
String? masterPubkey;
try {
@ -153,7 +153,7 @@ class CrossSigning {
// we don't care about signing other cross-signing keys
} else {
// okay, we'll sign a device key with our self signing key
selfSigningKey ??= base64.decode(await encryption.ssss
selfSigningKey ??= base64decodeUnpadded(await encryption.ssss
.getCached(EventTypes.CrossSigningSelfSigning) ??
'');
if (selfSigningKey.isNotEmpty) {
@ -163,7 +163,7 @@ class CrossSigning {
}
} else if (key is CrossSigningKey && key.usage.contains('master')) {
// we are signing someone elses master key
userSigningKey ??= base64.decode(await encryption.ssss
userSigningKey ??= base64decodeUnpadded(await encryption.ssss
.getCached(EventTypes.CrossSigningUserSigning) ??
'');
if (userSigningKey.isNotEmpty) {

View File

@ -18,6 +18,7 @@
import 'dart:convert';
import 'package:matrix/encryption/utils/base64_unpadded.dart';
import 'package:matrix/encryption/utils/stored_inbound_group_session.dart';
import 'package:olm/olm.dart' as olm;
import 'package:collection/collection.dart';
@ -49,7 +50,7 @@ class KeyManager {
BackupAlgorithm.mMegolmBackupV1Curve25519AesSha2) {
return false;
}
return keyObj.init_with_private_key(base64.decode(secret)) ==
return keyObj.init_with_private_key(base64decodeUnpadded(secret)) ==
info.authData['public_key'];
} catch (_) {
return false;
@ -579,7 +580,7 @@ class KeyManager {
return;
}
final privateKey =
base64.decode((await encryption.ssss.getCached(megolmKey))!);
base64decodeUnpadded((await encryption.ssss.getCached(megolmKey))!);
final decryption = olm.PkDecryption();
final info = await getRoomKeysBackupInfo();
String backupPubKey;
@ -723,7 +724,7 @@ class KeyManager {
return; // nothing to do
}
final privateKey =
base64.decode((await encryption.ssss.getCached(megolmKey))!);
base64decodeUnpadded((await encryption.ssss.getCached(megolmKey))!);
// decryption is needed to calculate the public key and thus see if the claimed information is in fact valid
final decryption = olm.PkDecryption();
final info = await getRoomKeysBackupInfo(false);

View File

@ -24,6 +24,7 @@ import 'dart:typed_data';
import 'package:base58check/base58.dart';
import 'package:crypto/crypto.dart';
import 'package:collection/collection.dart';
import 'package:matrix/encryption/utils/base64_unpadded.dart';
import '../matrix.dart';
import '../src/utils/crypto/crypto.dart' as uc;
@ -85,7 +86,7 @@ class SSSS {
[String? ivStr]) async {
Uint8List iv;
if (ivStr != null) {
iv = base64.decode(ivStr);
iv = base64decodeUnpadded(ivStr);
} else {
iv = Uint8List.fromList(uc.secureRandomBytes(16));
}
@ -108,15 +109,15 @@ class SSSS {
static Future<String> decryptAes(
_Encrypted data, Uint8List key, String name) async {
final keys = deriveKeys(key, name);
final cipher = base64.decode(data.ciphertext);
final cipher = base64decodeUnpadded(data.ciphertext);
final hmac = base64
.encode(Hmac(sha256, keys.hmacKey).convert(cipher).bytes)
.replaceAll(RegExp(r'=+$'), '');
if (hmac != data.mac.replaceAll(RegExp(r'=+$'), '')) {
throw Exception('Bad MAC');
}
final decipher =
await uc.aesCtr.encrypt(cipher, keys.aesKey, base64.decode(data.iv));
final decipher = await uc.aesCtr
.encrypt(cipher, keys.aesKey, base64decodeUnpadded(data.iv));
return String.fromCharCodes(decipher);
}

View File

@ -0,0 +1,13 @@
import 'dart:convert';
import 'dart:typed_data';
/// decodes base64
///
/// Dart's native [base64.decode] requires a padded base64 input String.
/// This function allows unpadded base64 too.
///
/// See: https://github.com/dart-lang/sdk/issues/39510
Uint8List base64decodeUnpadded(String s) {
final needEquals = (4 - (s.length % 4)) % 4;
return base64.decode(s + ('=' * needEquals));
}

View File

@ -26,6 +26,7 @@ import '../encryption.dart';
import '../ssss.dart';
import '../key_manager.dart';
import '../../matrix.dart';
import 'base64_unpadded.dart';
enum BootstrapState {
/// Is loading.
@ -385,7 +386,7 @@ class Bootstrap {
}
} else {
Logs().v('Get stored key...');
masterSigningKey = base64.decode(
masterSigningKey = base64decodeUnpadded(
await newSsssKey?.getStored(EventTypes.CrossSigningMasterKey) ??
'');
if (masterSigningKey.isEmpty) {

View File

@ -18,6 +18,8 @@
import 'dart:typed_data';
import 'dart:convert';
import 'package:matrix/encryption/utils/base64_unpadded.dart';
import 'crypto.dart';
class EncryptedFile {
@ -52,7 +54,7 @@ Future<Uint8List?> decryptFile(EncryptedFile input) async {
return null;
}
final key = base64.decode(base64.normalize(input.k));
final iv = base64.decode(base64.normalize(input.iv));
final key = base64decodeUnpadded(base64.normalize(input.k));
final iv = base64decodeUnpadded(base64.normalize(input.iv));
return await aesCtr.encrypt(input.data, key, iv);
}

View File

@ -0,0 +1,43 @@
/*
* Famedly Matrix SDK
* Copyright (C) 2022 Famedly GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import 'dart:convert';
import 'package:matrix/encryption/utils/base64_unpadded.dart';
import 'package:test/test.dart';
void main() {
group('Utils', () {
const base64input = 'foobar';
final utf8codec = Utf8Codec();
test('base64 padded', () {
final paddedBase64 = base64.encode(base64input.codeUnits);
final decodedPadded =
utf8codec.decode(base64decodeUnpadded(paddedBase64));
expect(decodedPadded, base64input, reason: 'Padded base64 decode');
});
test('base64 unpadded', () {
const unpaddedBase64 = 'Zm9vYmFy';
final decodedUnpadded =
utf8codec.decode(base64decodeUnpadded(unpaddedBase64));
expect(decodedUnpadded, base64input, reason: 'Unpadded base64 decode');
});
});
}