freedomlayer / offset Goto Github PK
View Code? Open in Web Editor NEWOffset payment engine
Home Page: https://www.offsetcredit.org
License: Other
Offset payment engine
Home Page: https://www.offsetcredit.org
License: Other
Currently the messages between IndexServer and IndexClient are as follows:
pub enum IndexServerToClient {
TimeHash(HashResult),
ResponseRoutes(ResponseRoutes),
}
pub enum IndexClientToServer {
MutationsUpdate(MutationsUpdate),
RequestRoutes(RequestRoutes),
}
This interface is used for two different functions:
Those two parts are unrelated. It might be possible that some user of the server may only want to use one function out of the two. For example: a user might only want to request routes (RequestRoutes/ResponseRoutes). In this case the user still has to deal with incoming TimeHash messages.
Cons of this design change are that index servers will now have two ports instead of one port facing clients. (Note that there is a third port facing other index servers).
I am trying to fix the problem in crypto
, I notice that some unwrap
usage here, and we should use a reasonable Error instead of unwrap
, and try to make this module unwrap
free.
BTW, I plan to use the define_ty
(Yep, I don't have a better name for it, define_fixed_bytes
?) to replace the boring job here. Including SymmetricKey, Salt, Signature, DhPublicKey, RandValue
, am I miss something?
Change all the request/response style interfaces with the Database module to use "send the sender" trick. In other words: Add to all request messages a field response_sender: mpsc::Sender. The Database module will then use the obtained sender to send the response.
In addition, we need to add the possibility of failure when trying to obtain something from the database. ResponseLoadFriendToken and ResponseLoadNeighborToken for example, should be able to indicate that no such Friend or no such Neighbor exist.
@kamyuentse: It is possible that some code at the Database module needs to be changed to allow these modifications. I will check this.
I added a new proposal for atomic payments.
The proposed new design protects the buyer against indefinite waiting for a payment to complete.
Any criticism and ideas are very welcome, and may be posted here.
Atomic payments proposal 05.05.2019
EDIT (Updated cancellation rules):
atomic payments proposal 08.05.2019
EDIT (Added fragmented multi-route payments):
atomic payments proposal 14.05.2019
We currently have the pinned rust nightly version in use scattered in a few places.
(1) The file: rust-toolchain
(See here: https://github.com/rust-lang/rustup.rs#the-toolchain-file)
(2) .travis.yml contains the used nightly version in three places (See snippet below).
This means that every time we bump rust nightly's version we have to modify all those places at the same time. Is it somehow possible for us to make .travis.yml find the pinned rust nightly version from the rust-toolchain
file automatically?
Snippet from .travis.yml
(Containing rust nightly version 3 times):
- env: TARGET=x86_64-unknown-linux-gnu CC=gcc-6 CXX=g++-6 RUSTFLAGS="-C link-dead-code"
os: linux
dist: trusty
rust: nightly-2019-06-07
addons:
apt:
packages:
- gcc-6
- g++-6
sources:
- ubuntu-toolchain-r-test
script:
- travis/trusty/pre/cargo-config.sh
- travis/trusty/pre/capnp.sh
- cargo fmt --all -- --check
# TODO: Enable these two lints when clippy bugs are solved:
- cargo clippy -- -A clippy::needless_lifetimes -A clippy::identity_conversion
# We add target dir so that kcov can find the test files to run:
- cargo test --target ${TARGET}
- travis/trusty/post/kcov/try-install.sh
- travis/trusty/post/kcov/run.sh
- env: TARGET=x86_64-apple-darwin
os: osx
rust: nightly-2019-06-07
osx_image: xcode9.4.1
before_script:
- brew install capnp
script:
- cargo test
- env: TARGET=x86_64-pc-windows-msvc
os: windows
# Used as a temporary fix for windows CI issue in travis.
# See: https://travis-ci.community/t/windows-instances-hanging-before-install/250/25
filter_secrets: false
rust: nightly-2019-06-07
...
EDIT: See: travis-ci/travis-build#1513
There seems to be some problem with the CI process.
I think that things leak between different branches. Some observations:
@pzmarzly : Do you have an idea what could the problem?
If you believe this is cache related (As mentioned earlier), I'm cool with removing the cache, at least for a while. I rather have slow CI over a wrong CI.
EDIT:
One example: When I currently enter: https://travis-ci.com/freedomlayer/offst (By clicking on the CI badge on master), I see the error from PR 205 written for branch master.
We currently manually convert between Capnp structures and Rust structures in offst-proto
.
This part of the code is very long, repetitive and error prone. Example from app_server/serialize.rs
:
fn ser_reset_friend_channel(
reset_friend_channel: &ResetFriendChannel,
reset_friend_channel_builder: &mut app_server_capnp::reset_friend_channel::Builder,
) {
write_public_key(
&reset_friend_channel.friend_public_key,
&mut reset_friend_channel_builder
.reborrow()
.init_friend_public_key(),
);
write_signature(
&reset_friend_channel.reset_token,
&mut reset_friend_channel_builder.reborrow().init_reset_token(),
);
}
fn deser_reset_friend_channel(
reset_friend_channel_reader: &app_server_capnp::reset_friend_channel::Reader,
) -> Result<ResetFriendChannel, SerializeError> {
Ok(ResetFriendChannel {
friend_public_key: read_public_key(&reset_friend_channel_reader.get_friend_public_key()?)?,
reset_token: read_signature(&reset_friend_channel_reader.get_reset_token()?)?,
})
}
This is something that can probably be solved using Rust procedural macros. I wasn't sure this is a viable solution until I have seen Libra's solution:
https://github.com/libra/libra/tree/master/common/proto_conv
Libra's relies on protobuf for serialization, but it seems to me like the macro conversion concept is similar.
The general plan is to have something like:
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[CapnpType(crate::funder::messages::AppPermissions)]
pub struct AppPermissions {
/// Can request routes
pub routes: bool,
/// Can send credits as a buyer
pub buyer: bool,
/// Can receive credits as a seller
pub seller: bool,
/// Can configure friends
pub config: bool,
}
Which automatically gives rust_object.into_capnp()
, and RustType::from_capnp(...)
.
Some things to consider:
List
. I'm not yet sure how to implement those.@pzmarzly : Is this something you would like to try out? I don't know much about proc macros at this point. If you have some time maybe you can explore a possible solution here.
EDIT: I will try to investigate what can be done here, hopefully I can come up with a good idea for implementation. I have to learn some stuff first.
In the future we might have to upgrade to upgrade CSwitch to use the newer Tokio Futures design. Currently CSwitch works with Futures 0.1.
I am still not sure if we want to do the upgrade only when Futures becomes stable (I heard that 1.0 might happen during 2018), or should already start doing it at Futures 0.2.
I was wondering what is the right way to do this kind of upgrade.
In the current design every node can pick one relay. The node then opens a "listen" connection to the relay and waits for connections from other nodes. In a sense, in the current design the relay is like a mail server that a user chooses to receive messages through.
The current design has two major issues:
lock-in: If a node wants to change his chosen relay, he has to notify all of his friends to make sure they can connect to him in the future. This creates a lock-in to a specific relay, making it difficult for a node to change relays.
lack of fault tolerance: Currently a node depends on a single relay to receive connections from remote nodes. If this relay is offline, the node will not be able to communicate with about half of his friends. (Half of the friends initiate connection, and half of the friends wait for connection. Therefore it will not be possible to communicate with the half the initiate connection).
A possible solution to these problem is as follows:
Every node will listen to multiple relays.
Whenever a node wants to change his relays list, the node will notify all his friends about the change through the token channel. The change will fully take place only one token channel message later, to make sure the remote side has received the change message.
Every node will maintain his wanted relays list, but will also maintain the relay lists as they were last sent to his friends.
For example, consider a node A with friends B and C:
A --- B
|
C
Whenever B wants to connect to A, B will first attempt to connect to R1. If R1 is down, or A is not found on R1, B will try R2. If connection through R2 fails, B will try R3.
Suppose that A wants to change his relays list to {R4, R5}. In this case A will notify B and C about the change through a token channel message. Suppose that B was online and was notified about the change, but C was not online. In this case A will now have to listen on R1, R2, R3, R4 and R5.
At some point C will connect to A. A will then notify C that the new relays list is {R4, R5}. A will disconnect from R1,R2,R3 only when a token channel message is received from C, proving that C has received the notification message about changing relays list.
Some places in the code base use Box::pin(...)
only to create a future that satisfies Pin requirements. One example from components/channeler/src/connect_pool.rs
:
async fn conn_attempt<RA, C, ET>(
friend_public_key: PublicKey,
address: RA,
mut client_connector: C,
mut encrypt_transform: ET,
canceler: oneshot::Receiver<()>,
) -> Option<RawConn>
where
RA: Eq,
C: FutTransform<Input = (RA, PublicKey), Output = Option<RawConn>> + Clone,
ET: FutTransform<Input = (PublicKey, RawConn), Output = Option<RawConn>> + Clone,
{
// TODO: How to remove this Box::pin?
let connect_fut = Box::pin(async move {
let raw_conn = await!(client_connector.transform((address, friend_public_key.clone())))?;
await!(encrypt_transform.transform((friend_public_key.clone(), raw_conn)))
});
// We either finish connecting, or got canceled in the middle:
select! {
connect_fut = connect_fut.fuse() => connect_fut,
_ = canceler.fuse() => None,
}
}
Box::pin(...)
is a runtime tradeoff we make only to satisfy the compiler. Is there a way to write this code without using Box::pin(...)
, and also without using unsafe tricks? It seems to me that we should be able to pin the future to the stack somehow, but I'm not sure if we are supposed to do this.
This is not high priority, but I was curious about this problem for a while. There are other places in the code that have the same problem, you can probably find them by searching for "Box::pin". (Note that there are some legitimate uses of Box::pin around the code too).
@pzmarzly : Do you think you can check this out?
Quickcheck is a rust crate: https://github.com/BurntSushi/quickcheck
It allows to test various predicates by automatically generating input, possibly in a smart way.
I am not fully sure how it works internally, but it looks very promising.
We might be able to use quickcheck to test some critical parts like the token channel state machine for the Networker and the Funder. I have no idea if this will increase the amount of time the tests run, but we might be able to separate the quickcheck tests from the rest of the tests.
This is an idea for the future, I wanted to make sure that I don't forget.
Many encryption schemes rely on an underlying communication layer that guarantees the order of sent messages. This underlying communication layer is usually TCP. Examples of such encryption schemes are SSH and TLS. Such encryption schemes fail to work over a communication layer that does not guarantee ordered arrival of messages.
Consider SSH for example: In order to avoid replay attacks, SSH uses an incrementing counter for each side of the communication. Every message contains an internal counter that increments for every message. The receiving side always checks the internal counter of incoming messages. If the counter value does not match the expected value, the message is discarded.
Consider a UDP like communication layer between two parties A and B, where there is no guarantee for the ordered arrival of messages: Messages may be dropped or arrive out of order. We want to set up an encrypted channel over such a communication layer.
In this case, we can not use the countermeasure used by SSH to defend against replay attacks. We can not use an incrementing counter because we can not be sure that messages arrive at the remote party at the same order they were sent.
This document presents a design for an anti-replay mechanism to be used when encrypting UDP style communication layers. The idea is due to Zur, and thus called Zur's sliding window.
Consider two parties A and B sending datagrams over a nonreliable UDP style communication layer. A and B both have public keys, each party knows the public key of the other party.
An attacker E can see all the communication on the wire between A and B, and can send messages on the wire. E also knows the public keys of A and B.
A -----+------- B
|
|
E
Assume that A and B somehow negotiated a symmetric key for symmetric encryption that allows authentication (If A encrypts a message, B can know for sure that the encrypted message was created by A).
If A naively sends encrypted datagrams to B, E will be able to resend those datagrams to B, making B think that A has sent those messages again. We call this attack replay attack.
Why is this a security problem? Consider this example: A sends a message to open a gate, a person then enters a building, and finally A sends a message to close the gate. If E could replay the "open gate" message, E could enter the building.
A and B need to find a way to send encrypted messages so that E will not be able to replay those messages.
We consider two sides: client (A) and server (B). The client is the side that has the ability to initiate a connection. In the case of IP networks, for example, the client knows the IP address of the server, but the server does not necessarily know the IP address of the client.
In other cases, it is possible that both sides can play the role of the client (If for example, both sides have static known addresses).
Setting up an encrypted connection is done through a 4-way handshake, as follows:
Where:
B considers the connection to be up once message ChannelReady is sent. A considers the connection to be up once message ChannelReady is received.
After the connection was set up, each party has two ends of channels, one sending end and one receiving end. The channel from A to B is called channelAId := sha512/256("Init" || dhPublicKeyA || dhPublicKeyB || randNonceA || randNonceB)[:16]
.
The Channel from B to A is called: channelBId := sha512/256("Accp" || dhPublicKeyB || dhPublicKeyA || randNonceB || randNonceA)[:16]
.
Note: channelXId is of size [16 bytes].
Both parties A and B create two symmetric keys for sending and receiving messages (keySaltA is used for sending messages from A to B, keySaltB is used or sending messages from B to A). We denote the symmetric keys by symmetricKeyAB (Sending from A to B) and symmetricKeyBA (Sending from B to A).
Pay special attention to the distinction between the concept of a connection setup and a channel. A connection setup between A and B creates two new distinct channels: A channel for sending messages from A to B, and a channel for sending messages from B to A. Each channel makes use of a different symmetric encryption key.
TODO: Dealing with message drops during connection setup? How to maintain state for connection setup?
Each party X maintains a maximum of 3 receiving ends of channels from a given remote party Y. This is called the channels carousel mechanism. Whenever a new receiving end is created from Y (For example, if X or Y initiated a new connection using the four-way handshake), X will erase the oldest receiving channel it has from Y, and replace it with the new receiving end.
The channels carousel ensures that if there are still some messages from Y sent through an old channel, floating in the network, when those messages arrive to X, X will be able to receive them. If X maintained only one
receiving end of a channel from Y to X, those messages would have been lost.
Why does the channel carousel have 3 receiving ends of channels and not just 2?
It is possible that both parties X and Y attempt to setup a connection at the same time. In this case, two new receiving ends of channels will be created for each party. If the capacity of the channel carousel was only 2, the simultaneous setup of two connections would have erased all of the old receiving ends, and messages that were previously sent might be lost.
Note that while maximum of 3 receiving ends of channels are maintained against every remote party, only one sending end of a channel is required against every remote party.
We still haven't explained what a channel end (receiving end or sending end) contains, and how we plan to avoid replay attacks using our mechanism. This is explained in the next sections.
Upon connection setup, every party sets up the following state in memory for
the sending end of a channel:
SendingEndState
The initial value of sendCounter is 1.
To send a message M to through a channel to the remote party, the party will
send:
Enc{
Key=symmetricKey,
Nonce=sendCounter,
AdditionalAuthenticated=channelId
}(M)
to the remote party. The sendCounter is then incremented by 1.
The following state is kept in memory for the receiving end of a channel:
ReceivingEndState
The initial value for recvCounter is 0.
recvCounter together with zurWindowBitmask allow representing the set of counters
that will be accepted for an incoming message:
(counter > recvCounter)
or
(
(recvCounter - 8*K <= counter < recvCounter)
and
(zurWindowBitmask[recvCounter - counter - 1] = 0)
)
Note: We consider the lsb of the zurWindowBitmask to be bit number 0.
This zur window bitmask construction allows more freedom for messages to arrive
out of order or not arrive at all. K is some parameter that we need to choose.
A reasonable value for K is 32 bytes = 256 bits. The bigger the value of K,
the more flexibility we allow for messages to be delayed or be dropped, for the
cost of using more memory for one channel receiving end.
Upon the receipt of an encrypted message, the receiving party does the
following:
1
from the right on the first bit shift).[recvCounter - K*8, recvCounter)
andzurWindowBitmask[recvCounter - counter - 1] = 1
Consider two parties A and B with a channel ChannelAB from A to B. A uses the channel to
send encrypted messages to B. If the machine that runs B shuts down or
malfunctions, it is possible that B will lose his state for the receiving end
of the channel ChannelAB.
A might keep sending encrypted messages to B on the ChannelAB channel, but B
will not be able to read those messages because he does not have the symmetric
key required to decrypt the messages, and he does not have a recvCounter in
place to make sure the messages are not being replayed.
In this case B will indicate to A that he has no information about the channel
ChannelAB using an UnknownChannel message:
UnknownChannel
B will send a new UnknownChannel message to A every time A attempts to send an
encrypted message to the channel ChannelAB.
Upon the receipt of this message, the party A will remove its sending end of
the ChannelAB channel. If A has the ability to initiate a connection, it might
attempt to setup a new connection with B.
cc @realcr I might need more details about the decryption/encryption, I haven't yet use AEAD before.
Add mandatory prefix (bytes slice?) for signing and verifying signatures.
This way a signature signed for one purpose could not be used as a signature for another purpose.
I tried to compile the project with the latest Rust nightly, and got lots of strange warnings that look like this:
warning: variable does not need to be mutable
--> src/security_module/client.rs:90:31
|
90 | handle.spawn(sm.then(|_| Ok(())));
| ^ help: remove this `mut`
warning: variable does not need to be mutable
--> src/security_module/client.rs:115:31
|
115 | handle.spawn(sm.then(|_| Ok(())));
| ^ help: remove this `mut`
error: variable does not need to be mutable
--> src/timer.rs:153:19
|
153 | .map(|_| tm.create_client())
| ^ help: remove this `mut`
It looks like a Rust bug to me, as no mut is there.
I just bumped Rust and clippy's versions.
On the new setup I get this warning when running cargo test --features=dev
:
warning: the clippy plugin is being deprecated, please use cargo clippy or rls with the clippy feature
@kamyuentse : Do you know of a non deprecated way to use clippy that still allows us to pin clippy's version?
The Channeler uses Fragmentos over UDP to communicate with other Channelers. Firewalls, NATs and routers have the tendency to shut down UDP communication if it is inactive for a while. Therefore I think that we should add some keepalive message to the Channeler. I explain below how I think we should add it.
An example use case could be as follows: Consider two nodes, C and S. C is a client behind a NAT. S is a server with a known IP address. Network configuration: C ----- NAT ------ S
Initial setup:
(public_key_S, ip_address_S)
.(public_key_C, None)
C sends the first handshake message to S (Using Fragmentos, over UDP). The first stop of the message is the NAT. It creates a new entry that maps a new port to the session began from C. It then forwards the message to S. S then sends back the second message in the 4-way handshake. When the message arrives at the NAT, it looks at its mapping table and it knows to forward the message back to C. Using this technique C and S can communicate, although C doesn't have a public IP address.
If C and S don't send any messages for a while, the NAT will remove the session entry due to timeout. As a result, if S sends a message to C, the message will not pass through the NAT.
A possible solution would be to constantly send keepalive messages between C and S. Both C and S send a keepalive message to the remote side if no message was sent for a while.
The current proposed Channeler's interface is at channeler_udp.capnp
. Quoting from this file:
# The data we encrypt. Contains random padding (Of variable length) together
# with actual data. This struct is used for any data we encrypt.
struct Plain {
randPadding @0: Data;
# Random padding: Makes traffic analysis harder.
content @1: Data;
}
struct ChannelerMessage {
union {
initChannel @0: InitChannel;
exchangePassive @1: ExchangePassive;
exchangeActive @2: ExchangeActive;
channelReady @3: ChannelReady;
unknownChannel @4: UnknownChannel;
encrypted @5: Data;
# Nonce is a 96 bit counter, Additional authenticated data is
# channelId.
}
}
I propose to put the keepalive message inside struct Plain
, as follows:
struct Plain {
randPadding @0: Data;
# Random padding: Makes traffic analysis harder.
union {
keepalive @1: Void;
content @2: Data;
}
}
Reasons for putting the keepalives inside an encrypted message:
I think that we should also use the keepalives to notice that a session with a remote Channeler is not active anymore. In this case we should clear the sender (symmetric key + incrementing nonce) and possibly attempt to initiate new secure channels using the 4-way handshake.
@kamyuentse: What do you think?
After stumbling upon unlinked file during development in #215 , I created a script to find other unlinked files. Here are the results for current master:
Stale files (safe to remove):
components/common/src/async_adapter.rs
components/common/src/frame_codec.rs
components/common/src/wait_spawner.rs
components/funder/src/credit_calc.rs
components/funder/src/freeze_guard_legacy.rs
components/stctrl/src/bin/stverify.rs
freeze_guard_legacy.rs
will be removed once #215 is merged. Please decide what to do with other files. I think it would be nice to have similar analysis for unused functions, but existing utilities (like warnalyzer) are not ready for codebases as large as offst yet.
We are currently using serialization to json file (using serde
) and writing to disk using the atomicwrites
crate.
Cons of the current design:
atomicwrites
crate.Required steps:
Hi there, I really like the idea of distributed ledger without proof of work. Really cool project.
I don't have much of a financial background, and I would like to better understand a scenario that would be common to me and a couple of my friends, if I was to present them the idea of the project.
If we setup a ledger between us, Alice, Bob and Charli, we would start with a 0 balance as documented. Alice provides Bob milk by 1 offst token in Brazil. Bob travels to meet Charli, which lives in Germany, and she also sells milk to him.
For how much should Charli charge for the milk? I thought of two scenarios, with some tradeoffs:
I could setup a wallet between Alice and Bob, and another between Bob and Charli. But that means that I could never exchange money between both wallets, otherwhise I would be converting a less valuable token to a more valuable token. We would rely on social norms to avoid this, and that does not seem like a good idea.
That looks like what bitcoin does, and could lead to a lot of volatibility on the token, and become a trading market on itself, if that is what I've understood.
Introducing exchange rates between wallets seems complicated for the system tho, and seems enough to separate the ledgers and repay the person to clear your debts. Or you could buy some currency using one ledger if the other person is willing to accept as well, selling the "good" using the system.
I've been looking at the project for a very short time, but I've didn't see any comments regarding different currencies and exchange rates. Is this something to be considered by the project at some point?
At crypto/dh.rs, inside derive_symmetric_key
there is the following code:
let shared_secret_array = match key_material_res {
Ok(key_material) => key_material,
_ => unreachable!(),
};
I think that we might be able to reach the unreachable code. (I wrote this code!) This might allow an attacker to acheive DoS, in case an invalid public key is provided during Diffie Hellman.
I think that a reasonable solution would be to change the function signature from:
pub fn derive_symmetric_key(&self, public_key: &DhPublicKey, salt: &Salt) -> SymmetricKey {
to
pub fn derive_symmetric_key(&self, public_key: &DhPublicKey, salt: &Salt) -> Result<SymmetricKey, ???> {
And return an Error on the case where we currently have unreachable!().
@kamyuentse : This could affect current Channeler code, so I assign this to you.
Tell me what you think.
This is a proposal for modification to the Networker's service interface (Against IndexerClient, Funder, AppManager). This modification to the Networker's interface is done in order to reflect the work it takes to set up a secure path to a remote Networker.
A secure path is an abstract idea used by the users of the Networker. Any component that takes service from the Networker may request a secure path to a remote node by providing a neighbors' route to the node. A secure path allows sending encrypted messages to a remote node.
A secure path is mapped to a neighbors route together with a channel sender. The channel sender allows to encrypt messages with the correct incrementing nonce. The neighbors route is necessary for routing the message to its destination by other Networkers on the way.
All the secure paths to a remote node use the same channel sender (with the same encryption key and the same incrementing nonce). However, it is possible to open several distinct secure paths that use different routes to the same remote node.
Secure paths should be used like a TCP connection. A message sent over a secure path will be retransmitted as necessary by intermediate Networkers. Therefore messages should not be retransmitted into a secure path.
With the new interface, using the Networker's service from a component (IndexerClient, Funder or AppManager) will be done as follows:
Component <---> Networker
The Component sends a RequestPath message to the Networker.
The Networker then checks if a current sender to the remote Networker exists. If so, an mpsc::Sender is sent back through the response_sender. If not, the Networker attemps to run a 4-way handshake against the remote Networker. If the 4-way handshake is completed successfuly, an mpsc::Sender is sent back to the component. If a failure occurs, a None is sent back to the component.
The component obtains an mpsc::Sender sender. A message can be sent by sending a RequestSendMessage through the sender to the Networker.
The Networker receives a RequestSendMessage. The RequestSendMessage contains a field: response_sender: mpsc::Sender. the Networker encrypts the message using the relevant key (This key was set up earlier during the 4-way handshake). Then this message is sent through a token channel. When a response is obtained from the token channel (either a success or failure), the Networker sends the response (success or failure) through the given mpsc::Sender.
The Component obtains the ResponseSendMessage for the sent message.
A secure path mpsc::Sender<RequestSendMessage>
may be closed from the Component side by closing the sender using Sink's close method This will signal to the Networker that the secure path is not used anymore.
A secure path can also be closed from the Networker's side by Dropping the receiver end. The Component will be notified of this when he tries to send a message through the secure path's sender.
The Networker will close his receiving end of the secure path in the following cases:
The current building process has lots of warnings that occur due to changes in Rust.
Some examples:
warning: use of deprecated item 'proto::proto_impl::channeler::channeler_capnp::exchange_passive::Builder::borrow': use reborrow() instead
--> src/proto/proto_impl/channeler.rs:63:50
|
63 | write_signature(&self.signature, &mut to.borrow().init_signature())?;
|
warning: use of deprecated item 'bytes::Buf::get_u64': use get_u64_be or get_u64_le
--> src/proto/proto_impl/common.rs:118:22
|
118 | to.set_x5(reader.get_u64::<BigEndian>());
| ^^^^^^^
and
warning: this feature has been stable since 1.26.0. Attribute no longer needed
--> src/lib.rs:4:12
|
4 | #![feature(i128_type)]
| ^^^^^^^^^
|
= note: #[warn(stable_features)] on by default
warning: this feature has been stable since 1.26.0. Attribute no longer needed
--> src/lib.rs:6:12
|
6 | #![feature(conservative_impl_trait, universal_impl_trait)]
| ^^^^^^^^^^^^^^^^^^^^^^^
warning: this feature has been stable since 1.26.0. Attribute no longer needed
--> src/lib.rs:6:37
|
6 | #![feature(conservative_impl_trait, universal_impl_trait)]
| ^^^^^^^^^^^^^^^^^^^^
@kamyuentse : I might take this one myself today, I can't see the compilation errors anymore because there are so many warnings (:
PR #68 stopped compiling with a somewhat unclear compilation errors. Here is an example:
error[E0658]: use of unstable library feature 'proc_macro' (see issue #38356)8: proc-macro2
--> /home/real/.cargo/registry/src/github.com-1ecc6299db9ec823/proc-macro2-0.4.8/src/unstable.rs:33:40
|
33 | let works = panic::catch_unwind(|| proc_macro::Span::call_site()).is_ok();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add #![feature(proc_macro)] to the crate attributes to enable
It is possible that some library has recently updated, However I am not sure which one, and what is the cause for this problem. I sent a message at issue #38356:
rust-lang/rust#38356 (comment)
Though I'm not fully sure it is related.
The same problem doesn't happen on master, I think that this might be related to the use of #[async]
style syntax.
@kamyuentse, @juchiast : Do you have an idea of how to fix this?
Currently RequestSendFundsOp
always contains the full route from the buyer to the seller. To (somewhat) protect the buyer's privacy, we propose to remove the nodes from the beginning of the route. Those nodes are not required for the correct operation of the protocol, and they expose the identity of the buyer.
Example: Consider the following network layout:
B -- C -- D -- E
Currently, a RequestSendFundsOp
message from B to E contains the route: B -- C -- D -- E
.
We denote the contents of the route in the RequestSendFundsOp
message received by the following nodes:
B -- C -- D -- E
.B -- C -- D -- E
B -- C -- D -- E
.In other words: when the message is forwarded from B to C, it still contains the full B -- C -- D -- E
. Next, when C forwards the message to D, it still contains the full route: B -- C -- D -- E
. Finally, when the message arrives at D, is still contains the full route: B -- C -- D -- E
. This reveals to E the public key of the buyer: B, hence violating the privacy of B.
Instead, we propose that when a node receives a RequestSendFundsOp
message, the message will contain as a route only the next nodes, and not include the node itself and the previous nodes. Continuing the example,
RequestSendFundsOp
message with the route C -- D -- E
.RequestSendFundsOp
message with the route D -- E
.RequestSendFundsOp
message with the route E
.This modification should provide some sort of privacy for the buyer (But not for the seller). Note that this will not provide "perfect" privacy for the buyer, for the following reasons:
That said, the proposed modification is a "quick win" for increasing the buyer privacy without too much work.
In the future, if this becomes important enough, we might be able to consider techniques like onion routing, where every node is given only the identity of the next node to forward a RequestSendFundsOp
, in encrypted form.
The proposed modification does not require changing the structure of RequestSendFundsOp
or any serialization code.
The major changes required are:
Part (1) Currently the ResponseSendFundsOp
contains the following capnp description:
struct ResponseSendFundsOp {
requestId @0: Uid;
destHashedLock @1: HashedLock;
randNonce @2: RandNonce;
signature @3: Signature;
# Signature{key=destinationKey}(
# sha512/256("FUNDS_RESPONSE") ||
# sha512/256(requestId || sha512/256(route) || randNonce) ||
# srcHashedLock ||
# destHashedLock ||
# destPayment ||
# totalDestPayment ||
# invoiceId
# )
# ...
}
Consider the signature description above (in comment). The signature currently contains sha512/256(route)
. This part needs to be removed, as with the proposed modification nodes later on the route will not be able to calculate this value. Therefore the route should not be included at all inside the signature. The new signature should be:
# Signature{key=destinationKey}(
# sha512/256("FUNDS_RESPONSE") ||
# sha512/256(requestId || randNonce) ||
# srcHashedLock ||
# destHashedLock ||
# destPayment ||
# totalDestPayment ||
# invoiceId
# )
# ...
This new signature form should be updated in all the comments that contain signature description:
offst-proto
.The code that computes this signature could be found in funder/signature_buff.rs
in offst-proto
. Make sure to modify all the places that calculate the signature. I think that the code in signature_buff.rs
contains some kind of code duplication for the computation of the signature. If you can refactor this part while you are there it could be an extra win.
After this part is finished, cargo test
should work correctly.
At this point the full route is still forwarded in the tests, but the signature does not include the full route any more.
Part (2) offst-funder logic
I think that the files that require modification are:
funder/src/mutual_credit/incoming.rs
(process_request_send_funds()
).outgoing.rs
does not require any modification.)funder/src/handler/handle_friend.rs
(Cutting one node from the route)funder/src/handler/handle_control.rs
(Cutting one node from the route)Stuff that should be changed:
During verification: Instead of searching ourselves and our friend on the route, assume that we are the first on the route (But not included), and the friend showed up earlier (also not included). For example, if the full route is B -- C -- D -- E -- F
and we are C
, we should only see D -- E -- F
.
Removing the first node from the route at handle_friend.rs
. This should be done at the same place where RequestSendFundsOp.left_fees
is decreased, at the end of handle_request_send_funds()
.
Removing the first node from the route given at control_create_transaction_inner()
in handle_control.rs
right before we push the request into the pending user requests queue. We need to do this because now the rest of the funder code now expects routes that do not begin with our own public key. Instead, the route should begin with the public key of the next node.
The Funder's code is a bit strange, because any mutation done on the Funder data must be atomic. This is intentional.
There is reasonably good test coverage for the code mentioned above, so the tests should help you be sure you are on the right track. Integration tests are really slow, and you don't need to run them all the time. You can run only the relevant tests for a specific crate, like this: crate test -p offst-funder
.
Be sure to send me a message if things get more complicated than the scope described in this issue.
After #207, I tried tweaking kcov settings further on, to understand how they work. I was running kcov on my local machine, using perf stat ./kcov-script.sh
.
--{include,exclude}-{path,pattern}=
options are used for filtering source files. Per kcov man page, they take comma-separated list of patterns. When no such options are present, all files are used (that includes dependencies downloaded in $HOME/.cargo
). Include and exclude are filtering list of files kcov finds in DWARF informations. They kinda work like this (Rust pseudocode):
fn include(files: Vec, text: String) -> Vec {
files.iter().filter(|x| x.contains(text)).collect()
}
fn exclude(files: Vec, text: String) -> Vec {
files.iter().filter(|x| !x.contains(text)).collect()
}
{include,exclude}_path
are much slower than {include,exclude}_pattern
. --include_path=components
adds 60 seconds to kcov process on my machine, and it would probably be more on Travis.
Since we use include_pattern=components
, only files which path contains components
are picked. If any dependency of offst ever happens to have components
directory, its test coverage would influence ours. That's why --exclude-path=/usr,~/.cargo
may be a good idea. --exclude-pattern=/usr/,/.cargo/
may be an even better one (see paragraph above), as long as we don't create folder called "usr" in offst codebase.
--verify
flag makes kcov 3-4 times slower, but it was needed to prevent it from segfaulting when working with Rust binaries. I tried removing it and nothing broke. I'm using the latest version of kcov (CI is not). This may be worth investigating.
kcov runs single-threaded. I tried using GNU parallel like this:
find \
target/${TARGET}/debug \
-maxdepth 1 -executable -type f \
| parallel \
-j0 \
'kcov ${FLAGS} target/kcov {}'
-j0
means "spawn as many threads as there are CPUs". This was not a good idea - kcov crashes a lot when multiple processes try to access target/kcov
at once.
So, given these pieces of information, I would suggest trying:
for exe in ${exes}; do
${HOME}/install/kcov-${TARGET}/bin/kcov \
--exclude-pattern=/usr/,/.cargo/ \
--include-pattern=/components/ \
target/kcov \
${exe}
done
Or maybe --exclude-pattern=/.cargo/
in case we ever create usr
directory. Or maybe just --exclude-path=/usr --exclude-pattern=/.cargo/
, it is costly, but it still should be better than what we have now, especially without --verify
.
For now I'm just writing what I know, asking for opinion.
I noticed some syntax error that happens during the travis build and test.
The source of the problem is probably before-script.sh, but I couldn't yet fix it. You might have an idea.
I couldn't make new pull requests pass the cargo clean && cargo clippy
on linux for some reason (They did pass on Mac though). I thought that it might be related to this issue.
I include the output from the Travis build attempts. Note that this error also occurs on successful builds.
@kamyuentse : You might have an idea. I think that this might be related to some recent modification I made to before-script, with the CLIPPY_VERSION
variable.
before_script
0.03s$ travis/trusty/before-script.sh
+CLIPPY_VERSION=0.0.206
+[[ -f .cargo/config ]]
+[[ ! -d .cargo ]]
+mkdir .cargo
+echo '[target.x86_64-unknown-linux-gnu]'
+echo 'linker= "gcc-6"'
+[[ -f /home/travis/.cargo/bin/cargo-clippy ]]
++cargo clippy --version
+[[ 0.0.206 -ne 0.0.206 ]]
travis/trusty/before-script.sh: line 17: [[: 0.0.206: syntax error: invalid arithmetic operator (error token is ".0.206")
EDIT:
Relevant code from before-script.sh:
echo "[target.$TARGET]" > .cargo/config
echo "linker= \"$CC\"" >> .cargo/config
if [[ -f "$HOME/.cargo/bin/cargo-clippy" ]]; then
if [[ "$(cargo clippy --version)" -ne "$CLIPPY_VERSION" ]]; then
cargo install --force clippy --vers "=$CLIPPY_VERSION"
fi
else
cargo install clippy --vers "=$CLIPPY_VERSION"
fi
Use cargo-audit
to make sure Offst does not depend on vulnerable or unmaintained crates.
See:
https://blog.rust-lang.org/inside-rust/2019/10/03/Keeping-secure-with-cargo-audit-0.9.html
For more information.
Currently, the CSwitch
uses the basic handwritten Error type to manage the error, I admit that's the most flexible way, but it would make us spent more time to write the code here if we want to trace the source of the error, or we got a useless error.
For example, in the crypto
, I notice we use ()
as the error in many places. But when the program runs into trouble, how to find the where is the exact location? If we leave this implementation, the caller must handle this error once the error happened, and assign a proper Error variant for it, this work should be done for each caller.
Another example is the Channeler
, in the Channeler
, we have some submodule, each of them may run into trouble. If we want to write a recoverable system, I think we should divide the error into:
fatal
── we should close the whole system.recoverable
── which means that module is already broken, we should find a way restart the module, if we can't restart this module, it would be a fatal error, we should close the whole system.ignorable
── an error happened, but it does not harm the whole module. For example, in futures 0.1
, when polling a stream and it returns an Error, it doesn't mean that the stream terminated, check rust-lang/futures-rs#206 for more details.The partition above might not accurate, just my idea.
If this is reasonable, we may discuss it, and add more things here.
Some crates in the Offst codebase still use serde_derive
. This is no longer required, see:
https://www.reddit.com/r/rust/comments/a8ykj8/til_you_dont_need_serde_derive_when_using_the/
We should update all the crates to have serde = { version = "1.0", features = ["derive"] }
instead in their Cargo.toml.
This issue can be solved by a beginner in Rust. Guidance can be given upon request.
uid.rs originally contained implementation of random generation of unique ids.
It was later renamed to uuid.rs, and the following comment was added:
///! A rough implementation of UUID v4.
I believe that this could cause confusion for the reader, as uuid.rs does not implement UUID v4, and does not intend to do this. I am not sure when was the uid.rs -> uuid.rs change happened, but using the logs I can confirm that the original name of the file was uid.rs:
$ git log | grep uid
Added uid.rs for generation of Uids.
think that we should remove the comment about UUID v4, and possibly rename it back to uid.rs to avoid confusion. I don't know if uuid.rs is currently in use by the Channeler code, so I did not make any modification yet.
Index servers do mainly two things:
The current implementation of Index servers is too optimistic, because index servers do not take into account the fees for sending credits along routes.
For example, with the current implementation, if a node B wants to send 10 credits to a remote node E, the following route may be proposed:
B -- C -- D -- E
Where the available capacity is as follows:
A route with exactly those capacities for sending credits is not sufficient for passing 10 credits, because fees needs to be paid to the intermediate nodes. Therefore, at least the following available capacities are required:
The implementation of the Index servers needs to be updated to take into account the payment fees to mediators.
The relevant code is at: components/index_server/src/graph/simple_capacity_graph.rs
, which relies on the implementation at bfs.rs
.
The current implementation relies on running a BFS algorithm to find a path of certain capacity from a source node to a destination node. We need to take into account the fees to the transaction mediators in this search algorithm.
Other relevant code is somewhat far away, at components/funder/src/credit_calc.rs
. This is the piece of code that contains the actual calculation for fees to mediators during a transaction. The crate offst-index-server should not have the crate offst-funder as a dependency, so maybe it could be a good idea to move credit_calc.rs to offst-proto, possibly now or in the future.
It might be possible to start a graph search algorithm that goes backwards (From the destination back to the source). The search algorithm can be somewhat like BFS, but we need to make sure that doing something like this is actually sound, and we can't possibly miss valid routes or return wrong routes.
This is a task that requires some prior research. Please write a short proposal of what you intend to do before you start coding, hopefully I might be able to say something useful that could help you out.
capnp-conv
was written very hastily, and some help is required with improving the code:
unimplemented!()
calls.capnp_conv
is a procedural macro mechanism allowing to automatically derive capnproto serialization and deserialization glue code between Rust structs and capnp structs.
Example for usage (From offst-proto):
#[capnp_conv(crate::funder_capnp::request_send_funds_op)]
#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct RequestSendFundsOp {
pub request_id: Uid,
pub src_hashed_lock: HashedLock,
pub route: FriendsRoute,
#[capnp_conv(with = Wrapper<u128>)]
pub dest_payment: u128,
#[capnp_conv(with = Wrapper<u128>)]
pub total_dest_payment: u128,
pub invoice_id: InvoiceId,
#[capnp_conv(with = Wrapper<u128>)]
pub left_fees: u128,
}
The user can then invoke: request_send_funds.to_capnp_bytes()
or RequestSendFunds::from_capnp_bytes(...)
The design of capnp_conv is inspired by proto_conv.
capnp_conv
is currently being used through the whole Offst codebase, mostly in offst-proto
.
We currently use an old version of the ring crate, due to problems with deterministic testing. (See #64).
It seems like other parts of the ring API has changed, and some work might be needed to use the latest version of ring. Most of the modifications should be at the offst-crypto crate.
If no other solutions comes up, we might need to give up the determinism in some of our cryptographic unit tests (For example, in offst-secure-channel). Relevant issue in the ring respository: briansmith/ring#661
The receiving end of the token channel inside Networker/Messenger needs to be reviewed.
When paying using an Offst node, a route to the destination node is required. Offst nodes obtain routes by sending a request to an index server. Currently the algorithm used at the index server is very naive, and might fail in some cases. We want to improve this algorithm.
Note: This issue continues issue #195.
A multi route between B and E is a set of routes (must be disjoint?) between B and E. For example:
B -- C -- E
B -- D -- F -- E
B -- G -- H --E
is a multi route between B and E.
To be a suitable multi route, the multi route between B and E should allow pushing enough credits from B to E. The index server's search algorithm must also take into consideration the fees B has to pay mediator nodes along the way.
Every node can specify a rate for forwarding payments for any of his immediate friends. We denote Rate{B,C}
to be the rate the node B set for the node C. (Only B controls this value).
Rates are of the form Rate {add: u32, mul: u32}
. Using a Rate
, one can calculate the fee for sending x
credits as rate.calc_fee(x) == (mul * x / 2**32) + add
Example: Consider the following network topology:
B -- C -- D -- E
\-- F
x
credits along the route B -- C
, no fees will be paid.x
credits along the route B -- C -- D
, C
will be paid Rate{C,B}.calc_fee(x)
x
credits along the route B -- C -- F
, C
will be paid Rate{C,B}.calc_fee(x)
x
credits along the route B -- C -- D -- E
, C
will be paid Rate{C,B}.calc_fee(x)
, and D
will be paid Rate{D,C}.calc_fee(x)
.The index server maintains capacity edges of the form:
pub struct CapacityEdge<C, T> {
pub capacity: CapacityPair<C>, // send and receive capacity pair.
pub rate: T,
}
For each pair of friend nodes. These edges are directed. This means that for two nodes B -- C
, the index server will keep a CapacityEdge for the edge (B,C)
and another one for the edge (C,B)
. Each edge represents the point of view of one of the nodes.
To get the "actual" send and receive capacity, one should take, for example, the minimum of send capacity of one side together with the receive capacity of the other side. This method should protect against one node reporting an inflated capacity to the index server.
The algorithm should allow searching the whole graph with one excluded edge. This is useful to be able to search for cycles. An example use case would be letting a node rebalance his debts. The way to do this is to find a cyclic route from a node to himself, and let him push credits along this route.
For example, consider the network layout:
/--...--\
B -- C -- D
Where the following pairs of nodes are friends: (B,C), (C,D). We also know that there is some route between B and D that doesn't include the edge (C,D).
Assume that C wants to move his debt with D to the node B. This can be done by letting C pay to himself along the route: C --> D --> ... -> B --> C
.
Without the edge excluding feature, if C naively asked for a route that goes from D to C, he would have gotten the route D -- C
, which is not suitable for debt rebalancing. However, if C uses the edge exclusion feature, he can ask to get a route from D to C that doesn't go through the directed (D,C) edge. This forces the index server to come up with a route that will allow C to rebalance his debts.
The search algorithm should find reasonably cheap multi-routes between pairs of nodes, reasonable quickly.
Single routes (Multi routes with only one route) are preferred over multi routes, unless (3).
It is preferred that every route in the multi route is not "saturated". For example: If we want to push 20 credits, and the index server returns two routes that have capacity of 10 credits each, we get a very tight situation (Every small change in the two routes might shut our opportunity to send funds). However, if we get three routes, each having 10 free credits, we have more space for changes. I have no idea how to put this into the algorithm. If it turns out difficult, it is not required.
In the current implementation of stctrl
, when a multi-route is obtained, we try to take some capacity from each of the routes while staying as far as possible from saturation (Using the metric of max(distance from saturation)
). You can check it here
There are some things I'm not sure about here, this is why I named it "thumb rules" and not "rules":
The most minimal solution should be able to return one multi-route whenever a valid one can be found.
I think that this has became a pretty self contained task. The code that should be changed is at:
components/index_server/graph
. bfs.rs
probably needs to change into some kind of advanced dijkstra, and in simple_capacity_graph.rs
we should change get_multi_routes()
and get_multi_route()
.
Now that offst compiles on stable, Readme.md can be changed to reflect that, no?
Posted on behalf of duxovni:
One feature I think it'd be really handy for the app to have is a UI for splitting bills, where a group of friends have a shared expense (split evenly or unevenly) and one of them fronts the money for it and gets IOUs from everyone else. That seems like something that'd fit really easily into this design and be very useful to a lot of people.
Some thought is required on how to make this work:
Hash locks are being used in the protocol to allow atomicity of payments. Currently hash locks are implemented using SHA512/256
.
It might be safer to use a different algorithm, for example bcrypt (As suggested in issue #196). On the other hand, it might degrade performance. I'm not yet sure what is the correct thing to do here.
The implementation can be found in components/crypto/src/hash_lock.rs
Note that changing the implementation might require changing the internal structure of HashedLock
(For example, if we want to add hash salt).
There are some parts in offst that attempt connections:
Currently each of those have a very basic backoff mechanism for reconnection after a failed connection attempt. This might cause DoS on a server.
We should change the backoff mechanism to exponential backoff, and find reasonable ways to test the new exponential backoff logic. Maybe there is a way to isolate this logic and reuse it in all clients that connect to servers in offst.
Offst's stcompact
binary is designed to work on mobile devices, specifically on Android.
We want to compile it for the following architectures:
More specifically, we want the CI to compile for those architectures during testing, and create a release binary when a new version tag is pushed to this repository.
Currently the automatic CI build system compiles and tests Offset for Windows, linux and macos.
Building for the Android architectures mentioned above is probably a bit different, because cross compilation is required.
I managed to perform cross compilation successfully on my machine (Ubuntu 18.04) by following the instructions from mozilla's website, with some minor modifications:
export NDK="/home/real/Android/Sdk/ndk/20.1.5948944"
ln -s $NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android26-clang $NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-clang
. Should add a similar symbolic link for the other two architectures.$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin
to $PATH
environment~/.cargo/config
, containing:[target.aarch64-linux-android]
ar = "/home/real/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar"
linker = "/home/real/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-clang"
[target.armv7-linux-androideabi]
ar = "/home/real/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-android26-ar"
linker = "/home/real/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-android26-clang"
[target.i686-linux-android]
ar = "/home/real/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android26-ar"
linker = "/home/real/Android/Sdk/ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/bin/i686-linux-android26-clang"
cargo build --target aarch64-linux-android --release
I need some help with making this work on the travis CI. My current gap in knowledge is how to install the android SDK and the NDK on the travis CI. Alternatively, maybe travis CI has special support for this case.
Might be related to:
rust-lang/rust-clippy#2718
crypto_rand.rs
is an unfortunate choice of name for the random utils module inside offst-crypto
crate.
For example, this is what current imports look like:
use crypto::crypto_rand::{CryptoRandom, RandValue};
A better name for this module might be just rand
, which should result in imports of this form:
use crypto::rand::{CryptoRandom, RandValue};
The crypto crate is used in most of offst crates. I suggest to begin with modifying the lowest crates first: offst-common
and offst-proto
, and only then move on to the rest of the crates.
At any time, running cargo test
should show any failures during the renaming. The more specific cargo test -p offst-XYZ
allows to test a specific crate (Given that all dependencies compile too).
This issue does not require great familiarity with the Offst project, and I think it can be solved by Rust beginners too. Guidance will be given to anyone who wants to solve this. Please send a message if you plan to work on this issue.
---- timer::tests::test_timer_basic stdout ----
thread 'timer::tests::test_timer_basic' panicked at 'assertion failed: start.elapsed() < dur * 17 * 12 / 10', src/timer.rs:163:21
This happened and make the CI failing in past submissions. Always occurred on the macOS.
The code at https://github.com/freedomlayer/offst/blob/master/components/crypto/src/rand.rs is very repetitive. Example:
impl RandGen for Salt {
fn rand_gen(crypt_rng: &impl CryptoRandom) -> Self {
let mut res = Self::default();
crypt_rng.fill(&mut res).unwrap();
res
}
}
impl RandGen for InvoiceId {
fn rand_gen(crypt_rng: &impl CryptoRandom) -> Self {
let mut res = Self::default();
crypt_rng.fill(&mut res).unwrap();
res
}
}
// ...
We can create a macro that does this automatically, possibly a macro that that can be used like this:
derive_rand_gen!(InvoiceId);
This issue is suitable for beginners with Rust and with the Offst project.
NOTE: If the protocol required changes, put this proposal aside, and fix the protocol first.
The Encryption Algorithm is fixed during the design of CSwitch
, in other words, this is a part of the CSwitch's protocol, which is the starting point of this proposal.
The design of this proposal has the following goals:
Currently, we have the following crypto primitive types:
Name | Size | impl traits |
---|---|---|
DhPrivateKey |
\ | \ |
DhPublicKey |
256 bits | Clone , Group1 |
PublicKey |
256 bits | Clone , Group1 , Group2 |
Signature |
512 bits | Group1 |
Salt |
256 bits | Clone , Group1 |
RandValue 1 |
128 bits | Clone , Group1 |
HashResult 2 |
256 bits | Clone , Group1 , Group2 |
Nonce |
96 bits | Group1 |
Note:
Group1
: Including AsRef<[u8]>
, Deref<Target=[u8]>
and DerefMut<Target=[u8]>
for reading and writing the underlying bytes conveniently.Group2
: Including PartialEq
, Eq
and Hash
, which means those types may be the key of a HashMap
, HashSet
, etc.1: Should we rename this type to RandNonce
, because I notice that we always use the rand_nonce
in where we hold this value.
2: The name of this type seems to obey the Rust's naming conventions, anyone has an idea about the name here?
Clone
For deriving the symmetric keys, each party needs to save a copy of those types during the handshake: DhPublicKey
, PublicKey
, Salt
. For calculating the channel identifier, they need the DhPublicKey
, RandValue
. They need impl Clone
, but I am not very sure whether we should do that. This information should be consumed and erase as soon as possible.
For the type mentioned above, I think we should manually implement Drop
for them, which erase the memory block where holds these value to ZERO. This will be done by the following code:
impl Drop for Nonce {
fn drop(&mut self) {
unsafe {
for b in &mut self.0 {
::std::ptr::write_volatile(b, 0);
}
}
}
}
Some traits are required when performing tests, i.e. Eq
, PartialEq
.etc. I think this can be done by #[cfg_attr(feature = "test", derive(Eq, PartialEq)))]
, this trick alse can be use for other types.
Comments and fill in the other details are welcome.
In the latest update to ring v0.13.0-alpha5 we get the following compilation error:
error[E0277]: the trait bound `crypto::test_utils::DummyRandom<R>: ring::private::Sealed` is not satisfied
--> src/crypto/test_utils.rs:23:14
|
23 | impl<R: Rng> SecureRandom for DummyRandom<R> {
| ^^^^^^^^^^^^ the trait `ring::private::Sealed` is not implemented for `crypto::test_utils::DummyRandom<R>`
See also issue briansmith/ring#661
I see three options to fix this:
RandomGenerator
. All of our code will work directly with RandomGenerator. This way we will not be affected by further changes to Ring's interface.FixedSliceRandom
from Ring. However, I think that the interface of this random generator is very uncomfortable, because we have to predict exactly the amount of random bytes that will be requested every time.Currently I think that option (1) is our best bet. @kamyuentse : What do you think?
We currently use the clap
crate for command line parsing. clap
uses strings to refer to arguments, which is not checked at compile time. The structopt builds over clap
and allows to define structs which automatically turn into command line arguments.
This should affect code in:
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.