0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-20 03:52:17 +02:00

bridge: Be more explicit about bridging u64 timestamps

u64 can't be represented as a primitive in Java or TypeScript (and for
the latter, Neon doesn't support bigint yet). However, for timestamps
represented as milliseconds, the integer-safe range of float64 still
covers more than 285,000 years, so it's reasonably safe to use
TypeScript's 'number' or Java's 'long' to represent these
ostensibly-64-bit values. Indicate this with a new Timestamp wrapper
type in the bridge layer.

In theory we could push this new Timestamp type down to the
libsignal-protocol crate. However, the protocol itself doesn't impose
any restrictions on the timestamp fields, so I figured it was best to
put it at the bridge layer, to indicate that it's about how Signal
specifically uses these fields.

This commit paves the way for being stricter about *other* u64 values
that might want to use the full 64-bit space.
This commit is contained in:
Jordan Rose 2021-10-29 16:27:45 -07:00
parent 0f27f82890
commit 2544f3d827
6 changed files with 78 additions and 41 deletions

17
node/Native.d.ts vendored
View File

@ -6,6 +6,7 @@
// WARNING: this file was automatically generated
type Uuid = Buffer;
type Timestamp = number;
export abstract class IdentityKeyStore {
_getIdentityKey(): Promise<PrivateKey>;
@ -51,10 +52,10 @@ export function CiphertextMessage_Serialize(obj: Wrapper<CiphertextMessage>): Bu
export function CiphertextMessage_Type(msg: Wrapper<CiphertextMessage>): number;
export function DecryptionErrorMessage_Deserialize(data: Buffer): DecryptionErrorMessage;
export function DecryptionErrorMessage_ExtractFromSerializedContent(bytes: Buffer): DecryptionErrorMessage;
export function DecryptionErrorMessage_ForOriginalMessage(originalBytes: Buffer, originalType: number, originalTimestamp: number, originalSenderDeviceId: number): DecryptionErrorMessage;
export function DecryptionErrorMessage_ForOriginalMessage(originalBytes: Buffer, originalType: number, originalTimestamp: Timestamp, originalSenderDeviceId: number): DecryptionErrorMessage;
export function DecryptionErrorMessage_GetDeviceId(obj: Wrapper<DecryptionErrorMessage>): number;
export function DecryptionErrorMessage_GetRatchetKey(m: Wrapper<DecryptionErrorMessage>): PublicKey | null;
export function DecryptionErrorMessage_GetTimestamp(obj: Wrapper<DecryptionErrorMessage>): number;
export function DecryptionErrorMessage_GetTimestamp(obj: Wrapper<DecryptionErrorMessage>): Timestamp;
export function DecryptionErrorMessage_Serialize(obj: Wrapper<DecryptionErrorMessage>): Buffer;
export function Fingerprint_DisplayString(obj: Wrapper<Fingerprint>): string;
export function Fingerprint_New(iterations: number, version: number, localIdentifier: Buffer, localKey: Wrapper<PublicKey>, remoteIdentifier: Buffer, remoteKey: Wrapper<PublicKey>): Fingerprint;
@ -113,7 +114,7 @@ export function SealedSenderDecryptionResult_GetDeviceId(obj: Wrapper<SealedSend
export function SealedSenderDecryptionResult_GetSenderE164(obj: Wrapper<SealedSenderDecryptionResult>): string | null;
export function SealedSenderDecryptionResult_GetSenderUuid(obj: Wrapper<SealedSenderDecryptionResult>): string;
export function SealedSenderDecryptionResult_Message(obj: Wrapper<SealedSenderDecryptionResult>): Buffer;
export function SealedSender_DecryptMessage(message: Buffer, trustRoot: Wrapper<PublicKey>, timestamp: number, localE164: string | null, localUuid: string, localDeviceId: number, sessionStore: SessionStore, identityStore: IdentityKeyStore, prekeyStore: PreKeyStore, signedPrekeyStore: SignedPreKeyStore): Promise<SealedSenderDecryptionResult>;
export function SealedSender_DecryptMessage(message: Buffer, trustRoot: Wrapper<PublicKey>, timestamp: Timestamp, localE164: string | null, localUuid: string, localDeviceId: number, sessionStore: SessionStore, identityStore: IdentityKeyStore, prekeyStore: PreKeyStore, signedPrekeyStore: SignedPreKeyStore): Promise<SealedSenderDecryptionResult>;
export function SealedSender_DecryptToUsmc(ctext: Buffer, identityStore: IdentityKeyStore, ctx: null): Promise<UnidentifiedSenderMessageContent>;
export function SealedSender_Encrypt(destination: Wrapper<ProtocolAddress>, content: Wrapper<UnidentifiedSenderMessageContent>, identityKeyStore: IdentityKeyStore, ctx: null): Promise<Buffer>;
export function SealedSender_MultiRecipientEncrypt(recipients: Wrapper<ProtocolAddress>[], recipientSessions: Wrapper<SessionRecord>[], content: Wrapper<UnidentifiedSenderMessageContent>, identityKeyStore: IdentityKeyStore, ctx: null): Promise<Buffer>;
@ -121,15 +122,15 @@ export function SealedSender_MultiRecipientMessageForSingleRecipient(encodedMult
export function SenderCertificate_Deserialize(data: Buffer): SenderCertificate;
export function SenderCertificate_GetCertificate(obj: Wrapper<SenderCertificate>): Buffer;
export function SenderCertificate_GetDeviceId(obj: Wrapper<SenderCertificate>): number;
export function SenderCertificate_GetExpiration(obj: Wrapper<SenderCertificate>): number;
export function SenderCertificate_GetExpiration(obj: Wrapper<SenderCertificate>): Timestamp;
export function SenderCertificate_GetKey(obj: Wrapper<SenderCertificate>): PublicKey;
export function SenderCertificate_GetSenderE164(obj: Wrapper<SenderCertificate>): string | null;
export function SenderCertificate_GetSenderUuid(obj: Wrapper<SenderCertificate>): string;
export function SenderCertificate_GetSerialized(obj: Wrapper<SenderCertificate>): Buffer;
export function SenderCertificate_GetServerCertificate(cert: Wrapper<SenderCertificate>): ServerCertificate;
export function SenderCertificate_GetSignature(obj: Wrapper<SenderCertificate>): Buffer;
export function SenderCertificate_New(senderUuid: string, senderE164: string | null, senderDeviceId: number, senderKey: Wrapper<PublicKey>, expiration: number, signerCert: Wrapper<ServerCertificate>, signerKey: Wrapper<PrivateKey>): SenderCertificate;
export function SenderCertificate_Validate(cert: Wrapper<SenderCertificate>, key: Wrapper<PublicKey>, time: number): boolean;
export function SenderCertificate_New(senderUuid: string, senderE164: string | null, senderDeviceId: number, senderKey: Wrapper<PublicKey>, expiration: Timestamp, signerCert: Wrapper<ServerCertificate>, signerKey: Wrapper<PrivateKey>): SenderCertificate;
export function SenderCertificate_Validate(cert: Wrapper<SenderCertificate>, key: Wrapper<PublicKey>, time: Timestamp): boolean;
export function SenderKeyDistributionMessage_Create(sender: Wrapper<ProtocolAddress>, distributionId: Uuid, store: SenderKeyStore, ctx: null): Promise<SenderKeyDistributionMessage>;
export function SenderKeyDistributionMessage_Deserialize(data: Buffer): SenderKeyDistributionMessage;
export function SenderKeyDistributionMessage_GetChainId(obj: Wrapper<SenderKeyDistributionMessage>): number;
@ -180,8 +181,8 @@ export function SignedPreKeyRecord_GetId(obj: Wrapper<SignedPreKeyRecord>): numb
export function SignedPreKeyRecord_GetPrivateKey(obj: Wrapper<SignedPreKeyRecord>): PrivateKey;
export function SignedPreKeyRecord_GetPublicKey(obj: Wrapper<SignedPreKeyRecord>): PublicKey;
export function SignedPreKeyRecord_GetSignature(obj: Wrapper<SignedPreKeyRecord>): Buffer;
export function SignedPreKeyRecord_GetTimestamp(obj: Wrapper<SignedPreKeyRecord>): number;
export function SignedPreKeyRecord_New(id: number, timestamp: number, pubKey: Wrapper<PublicKey>, privKey: Wrapper<PrivateKey>, signature: Buffer): SignedPreKeyRecord;
export function SignedPreKeyRecord_GetTimestamp(obj: Wrapper<SignedPreKeyRecord>): Timestamp;
export function SignedPreKeyRecord_New(id: number, timestamp: Timestamp, pubKey: Wrapper<PublicKey>, privKey: Wrapper<PrivateKey>, signature: Buffer): SignedPreKeyRecord;
export function SignedPreKeyRecord_Serialize(obj: Wrapper<SignedPreKeyRecord>): Buffer;
export function UnidentifiedSenderMessageContent_Deserialize(data: Buffer): UnidentifiedSenderMessageContent;
export function UnidentifiedSenderMessageContent_GetContentHint(m: Wrapper<UnidentifiedSenderMessageContent>): number;

View File

@ -6,6 +6,7 @@
// WARNING: this file was automatically generated
type Uuid = Buffer;
type Timestamp = number;
export abstract class IdentityKeyStore {
_getIdentityKey(): Promise<PrivateKey>;

View File

@ -350,6 +350,20 @@ impl ResultTypeInfo for Option<u32> {
}
}
impl SimpleArgTypeInfo for crate::protocol::Timestamp {
type ArgType = u64;
fn convert_from(foreign: Self::ArgType) -> SignalFfiResult<Self> {
Ok(Self::from_millis(foreign))
}
}
impl ResultTypeInfo for crate::protocol::Timestamp {
type ResultType = u64;
fn convert_into(self) -> SignalFfiResult<Self::ResultType> {
Ok(self.as_millis())
}
}
/// A marker for Rust objects exposed as opaque pointers.
///
/// When we do this, we hand the lifetime over to the app. Since we don't know how long the object
@ -503,6 +517,7 @@ macro_rules! ffi_arg_type {
(Option<String>) => (*const libc::c_char);
(Option<&str>) => (*const libc::c_char);
(Context) => (*mut libc::c_void);
(Timestamp) => (u64);
(Uuid) => (*const [u8; 16]);
(&[& $typ:ty]) => (*const *const $typ);
(&mut dyn $typ:ty) => (*const paste!(ffi::[<Ffi $typ Struct>]));
@ -537,6 +552,7 @@ macro_rules! ffi_result_type {
(Option<String>) => (*const libc::c_char);
(Option<&str>) => (*const libc::c_char);
(Option<$typ:ty>) => (*mut $typ);
(Timestamp) => (u64);
(Uuid) => ([u8; 16]);
( $typ:ty ) => (*mut $typ);
}

View File

@ -166,16 +166,16 @@ impl<'a> SimpleArgTypeInfo<'a> for Option<u32> {
///
/// Negative `long` values are *not* reinterpreted as large `u64` values.
/// Note that this is different from the implementation of [`ResultTypeInfo`] for `u64`.
impl<'a> SimpleArgTypeInfo<'a> for u64 {
impl<'a> SimpleArgTypeInfo<'a> for crate::protocol::Timestamp {
type ArgType = jlong;
fn convert_from(_env: &JNIEnv, foreign: jlong) -> SignalJniResult<Self> {
if foreign < 0 {
return Err(SignalJniError::IntegerOverflow(format!(
"{} to u64",
"{} to Timestamp (u64)",
foreign
)));
}
Ok(foreign as u64)
Ok(Self::from_millis(foreign as u64))
}
}
@ -436,14 +436,14 @@ impl ResultTypeInfo for Option<u32> {
}
}
/// Reinterprets the bits of the `u64` as a Java `long`.
/// Reinterprets the bits of the timestamp's `u64` as a Java `long`.
///
/// Note that this is different from the implementation of [`ArgTypeInfo`] for `u64`.
impl ResultTypeInfo for u64 {
/// Note that this is different from the implementation of [`ArgTypeInfo`] for `Timestamp`.
impl ResultTypeInfo for crate::protocol::Timestamp {
type ResultType = jlong;
fn convert_into(self, _env: &JNIEnv) -> SignalJniResult<Self::ResultType> {
// Note that we don't check bounds here.
Ok(self as jlong)
Ok(self.as_millis() as jlong)
}
fn convert_into_jobject(_signal_jni_result: &SignalJniResult<Self::ResultType>) -> JObject {
JObject::null()
@ -849,9 +849,6 @@ macro_rules! jni_arg_type {
(Option<u32>) => {
jni::jint
};
(u64) => {
jni::jlong
};
(String) => {
jni::JString
};
@ -870,6 +867,9 @@ macro_rules! jni_arg_type {
(Context) => {
jni::JObject
};
(Timestamp) => {
jni::jlong
};
(Uuid) => {
jni::JavaUUID
};
@ -932,9 +932,6 @@ macro_rules! jni_result_type {
(u32) => {
jni::jint
};
(u64) => {
jni::jlong
};
(Option<u32>) => {
jni::jint
};
@ -947,6 +944,9 @@ macro_rules! jni_result_type {
(Uuid) => {
jni::JavaReturnUUID
};
(Timestamp) => {
jni::jlong
};
(&[u8]) => {
jni::jbyteArray
};

View File

@ -250,14 +250,14 @@ const MAX_SAFE_JS_INTEGER: f64 = 9007199254740991.0;
/// Converts non-negative numbers up to [`Number.MAX_SAFE_INTEGER`][].
///
/// [`Number.MAX_SAFE_INTEGER`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
impl SimpleArgTypeInfo for u64 {
impl SimpleArgTypeInfo for crate::protocol::Timestamp {
type ArgType = JsNumber;
fn convert_from(cx: &mut FunctionContext, foreign: Handle<Self::ArgType>) -> NeonResult<Self> {
let value = foreign.value(cx);
if !can_convert_js_number_to_int(value, 0.0..=MAX_SAFE_JS_INTEGER) {
return cx.throw_range_error(format!("cannot convert {} to u64", value));
return cx.throw_range_error(format!("cannot convert {} to Timestamp (u64)", value));
}
Ok(value as u64)
Ok(Self::from_millis(value as u64))
}
}
@ -527,17 +527,17 @@ impl<'a> ResultTypeInfo<'a> for bool {
/// Converts non-negative values up to [`Number.MAX_SAFE_INTEGER`][].
///
/// [`Number.MAX_SAFE_INTEGER`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER
impl<'a> ResultTypeInfo<'a> for u64 {
impl<'a> ResultTypeInfo<'a> for crate::protocol::Timestamp {
type ResultType = JsNumber;
fn convert_into(self, cx: &mut impl Context<'a>) -> NeonResult<Handle<'a, Self::ResultType>> {
let result = self as f64;
let result = self.as_millis() as f64;
if result > MAX_SAFE_JS_INTEGER {
cx.throw_range_error(format!(
"precision loss during conversion of {} to f64",
self
self.as_millis()
))?;
}
Ok(cx.number(self as f64))
Ok(cx.number(result))
}
}

View File

@ -38,6 +38,25 @@ bridge_handle!(SignedPreKeyRecord);
bridge_handle!(UnidentifiedSenderMessageContent, clone = false);
bridge_handle!(SealedSenderDecryptionResult, ffi = false, jni = false);
#[derive(Clone, Copy, Debug)]
pub(crate) struct Timestamp(u64);
impl Timestamp {
pub(crate) fn from_millis(millis: u64) -> Self {
Self(millis)
}
pub(crate) fn as_millis(self) -> u64 {
self.0
}
}
impl From<u64> for Timestamp {
fn from(value: u64) -> Self {
Self::from_millis(value)
}
}
#[bridge_fn_buffer(ffi = false)]
fn HKDF_DeriveSecrets(
output_length: u32,
@ -417,7 +436,7 @@ fn SenderKeyDistributionMessage_GetSignatureKey(
}
bridge_deserialize!(DecryptionErrorMessage::try_from);
bridge_get!(DecryptionErrorMessage::timestamp -> u64);
bridge_get!(DecryptionErrorMessage::timestamp -> Timestamp);
bridge_get!(DecryptionErrorMessage::device_id -> u32);
bridge_get_buffer!(
DecryptionErrorMessage::serialized as Serialize -> &[u8],
@ -433,7 +452,7 @@ fn DecryptionErrorMessage_GetRatchetKey(m: &DecryptionErrorMessage) -> Option<Pu
fn DecryptionErrorMessage_ForOriginalMessage(
original_bytes: &[u8],
original_type: u8,
original_timestamp: u64,
original_timestamp: Timestamp,
original_sender_device_id: u32,
) -> Result<DecryptionErrorMessage> {
let original_type = CiphertextMessageType::try_from(original_type).map_err(|_| {
@ -442,7 +461,7 @@ fn DecryptionErrorMessage_ForOriginalMessage(
DecryptionErrorMessage::for_original(
original_bytes,
original_type,
original_timestamp,
original_timestamp.as_millis(),
original_sender_device_id,
)
}
@ -528,20 +547,20 @@ bridge_get_buffer!(
jni = "SignedPreKeyRecord_1GetSerialized"
);
bridge_get!(SignedPreKeyRecord::id -> u32);
bridge_get!(SignedPreKeyRecord::timestamp -> u64);
bridge_get!(SignedPreKeyRecord::timestamp -> Timestamp);
bridge_get!(SignedPreKeyRecord::public_key -> PublicKey);
bridge_get!(SignedPreKeyRecord::private_key -> PrivateKey);
#[bridge_fn]
fn SignedPreKeyRecord_New(
id: u32,
timestamp: u64,
timestamp: Timestamp,
pub_key: &PublicKey,
priv_key: &PrivateKey,
signature: &[u8],
) -> SignedPreKeyRecord {
let keypair = KeyPair::new(*pub_key, *priv_key);
SignedPreKeyRecord::new(id, timestamp, &keypair, signature)
SignedPreKeyRecord::new(id, timestamp.as_millis(), &keypair, signature)
}
bridge_deserialize!(PreKeyRecord::deserialize);
@ -593,7 +612,7 @@ bridge_get_buffer!(SenderCertificate::certificate -> &[u8]);
bridge_get_buffer!(SenderCertificate::signature -> &[u8]);
bridge_get!(SenderCertificate::sender_uuid -> &str);
bridge_get!(SenderCertificate::sender_e164 -> Option<&str>);
bridge_get!(SenderCertificate::expiration -> u64);
bridge_get!(SenderCertificate::expiration -> Timestamp);
bridge_get!(SenderCertificate::sender_device_id as GetDeviceId -> u32);
bridge_get!(SenderCertificate::key -> PublicKey);
@ -601,9 +620,9 @@ bridge_get!(SenderCertificate::key -> PublicKey);
fn SenderCertificate_Validate(
cert: &SenderCertificate,
key: &PublicKey,
time: u64,
time: Timestamp,
) -> Result<bool> {
cert.validate(key, time)
cert.validate(key, time.as_millis())
}
#[bridge_fn]
@ -617,7 +636,7 @@ fn SenderCertificate_New(
sender_e164: Option<String>,
sender_device_id: u32,
sender_key: &PublicKey,
expiration: u64,
expiration: Timestamp,
signer_cert: &ServerCertificate,
signer_key: &PrivateKey,
) -> Result<SenderCertificate> {
@ -628,7 +647,7 @@ fn SenderCertificate_New(
sender_e164,
*sender_key,
sender_device_id,
expiration,
expiration.as_millis(),
signer_cert.clone(),
signer_key,
&mut rng,
@ -1080,7 +1099,7 @@ async fn SealedSessionCipher_DecryptToUsmc(
async fn SealedSender_DecryptMessage(
message: &[u8],
trust_root: &PublicKey,
timestamp: u64,
timestamp: Timestamp,
local_e164: Option<String>,
local_uuid: String,
local_device_id: u32,
@ -1092,7 +1111,7 @@ async fn SealedSender_DecryptMessage(
sealed_sender_decrypt(
message,
trust_root,
timestamp,
timestamp.as_millis(),
local_e164,
local_uuid,
local_device_id,