fix: Allow unpadded base64 decoding
This commit is contained in:
parent
d31a594068
commit
cab03aa73b
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue