matrix_sdk_qrcode/utils.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
// Copyright 2021 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use qrcode::{bits::Bits, EcLevel, QrCode, Version};
use ruma_common::serde::Base64;
use vodozemac::Ed25519PublicKey;
use crate::error::EncodingError;
pub(crate) const HEADER: &[u8] = b"MATRIX";
pub(crate) const VERSION: u8 = 0x2;
pub(crate) const MAX_MODE: u8 = 0x2;
pub(crate) const MIN_SECRET_LEN: usize = 8;
pub(crate) fn to_bytes(
mode: u8,
flow_id: &str,
first_key: Ed25519PublicKey,
second_key: Ed25519PublicKey,
shared_secret: &Base64,
) -> Result<Vec<u8>, EncodingError> {
let flow_id_len: u16 = flow_id.len().try_into()?;
let flow_id_len = flow_id_len.to_be_bytes();
let data = [
HEADER,
&[VERSION],
&[mode],
flow_id_len.as_ref(),
flow_id.as_bytes(),
first_key.as_bytes(),
second_key.as_bytes(),
shared_secret.as_bytes(),
]
.concat();
Ok(data)
}
pub(crate) fn to_qr_code(
mode: u8,
flow_id: &str,
first_key: Ed25519PublicKey,
second_key: Ed25519PublicKey,
shared_secret: &Base64,
) -> Result<QrCode, EncodingError> {
let data = to_bytes(mode, flow_id, first_key, second_key, shared_secret)?;
// Mobile clients seem to have trouble decoding the QR code that gets
// generated by `QrCode::new()` it seems to add a couple of data segments
// with different data modes/types. The parsers seem to assume a single
// data type and since we start with an ASCII `MATRIX` header the rest of
// the data gets treated as a string as well.
//
// We make sure that there isn't an ECI bit set and we just push the bytes,
// this seems to help since the decoder doesn't assume an encoding and
// treats everything as raw bytes.
let mut bits = Bits::new(Version::Normal(7));
bits.push_byte_data(&data)?;
bits.push_terminator(EcLevel::L)?;
Ok(QrCode::with_bits(bits, EcLevel::L)?)
}