NAME
Protocol::OTR::Channel - Off-the-Record secure messaging protocol
VERSION
version 0.01
SYNOPSIS
use Protocol::OTR qw( :constants );
my $otr = Protocol::OTR->new(
{
privkeys_file => "otr.private_key",
contacts_file => "otr.fingerprints",
instance_tags_file => "otr.instance_tags",
}
);
# find or create account
my $alice = $otr->account('alice@domain', 'prpl-jabber');
# find or create contact known by $alice
my $bob = $alice->contact('bob@domain');
# create secure channel to Bob
my $channel = $bob->channel(
{
policy => ...,
max_message_size => ...,
on_write => sub { ... },
on_read => sub { ... },
on_gone_secure => sub { ... },
on_gone_insecure => sub { ... },
on_still_secure => sub { ... },
on_unverified_fingerprint => sub { ... },
on_symkey => sub { ... },
on_timer => sub { ... },
on_smp => sub { ... },
on_error => sub { ... },
on_event => sub { ... },
on_smp_event => sub { ... },
on_before_encrypt => sub { ... },
on_after_decrypt => sub { ... },
on_is_contact_logged_in => sub { ... },
}
);
DESCRIPTION
Protocol::OTR::Contact represents the OTR contact.
METHODS
account
my $account = $channel->account();
Returns channel's Protocol::OTR::Account object.
contact
my $contact = $channel->contact();
Returns channel's Protocol::OTR::Contact object.
init
$channel->init();
Send OTR default query message to initialize secure session.
'<b>'. $channel->account->name .'</b> has requested an '
.'<a href="http://otr.cypherpunks.ca/">Off-the-Record '
.'private conversation</a>. However, you do not have a plugin '
.'to support that.\nSee <a href="http://otr.cypherpunks.ca/">'
.'http://otr.cypherpunks.ca/</a> for more information.'
refresh
$channel->refresh();
Refreshes current authentication keys.
finish
$channel->finish();
Finish all sessions within the channel.
create_symkey
my $symkey = $channel->create_symkey( $use, $use_for );
Returns symmetric key agreed with other party. $use
is an integer (1 is reserved for future support of file transfers), while $use_for
is a message how the symmetric key will be used.
Generate key for symmetric encryption:
# Alice
my $crypt_cipher = "Blowfish";
my $crypt_key = $channel->create_symkey( 2, $crypt_cipher );
my $crypt = Crypt::CBC->new(
-key => $crypt_key,
-cipher => $crypt_cipher,
);
# Bob
on_symkey => sub {
my ($c, $symkey, $use, $use_for) = @_;
my $crypt_cipher = $use_for;
my $crypt_key = $symkey;
my $crypt = Crypt::CBC->new(
-key => $crypt_key,
-cipher => $crypt_cipher,
);
}
ping
$channel->ping();
Call this function every so often, either as directed by the "on_timer" callback or every minute if the callback is not implemented.
See "on_timer" for more details.
smp_verify
$channel->smp_verify( $answer, [ $question ]);
Verify identity of the contact using Socialist Millionaires' Protocol (SMP). Contact is required to respond with the expected $answer
, optional $question
may be provided.
smp_abort
$channel->smp_abort();
Abort the SMP when error occured.
write
$channel->write( $message );
Send message over the secure channel. The encrypted message ready for sending will be passed to "on_write" callback to transport.
read
$channel->read( $input );
Handle a message just received from the network. It is safe to pass all received messages to this routine. Decrypted messages will be passed to "on_read" callback.
sessions
my @sessions = $channel->sessions();
Returns a list of session IDs (instags) that were used by the contact in this channel.
current_session
my $current_session_id = $channel->current_session();
Returns currently used by the contact session ID (instag) in this channel.
select_session
$channel->select_session( $id );
Selects provided session id as the current session. Returns false if session id is not known.
One of special session selectors (exported via ":instags" in Protocol::OTR) can be also used.
CALLBACKS AND OPTIONS
my $channel = $contact->channel(
{
policy => ...,
max_message_size => ...,
on_write => sub { ... },
on_read => sub { ... },
on_gone_secure => sub { ... },
on_gone_insecure => sub { ... },
on_still_secure => sub { ... },
on_unverified_fingerprint => sub { ... },
on_symkey => sub { ... },
on_timer => sub { ... },
on_smp => sub { ... },
on_error => sub { ... },
on_event => sub { ... },
on_smp_event => sub { ... },
on_before_encrypt => sub { ... },
on_after_decrypt => sub { ... },
on_is_contact_logged_in => sub { ... },
}
);
policy
my $channel = $contact->channel(
{
policy => POLICY_OPPORTUNISTIC,
...
}
);
Select the default OTR policy, see ":policies" in Protocol::OTR for details.
max_message_size
my $channel = $contact->channel(
{
max_message_size => 10 * 1024,
...
}
);
Define the maximum message size handled by protocol. Messages larger then this value will be split and delivered separetely.
Following defaults are used based on the protocol:
prpl-msn: 1409
prpl-icq: 2346
prpl-aim: 2343
prpl-yahoo: 799
prpl-gg: 1999
prpl-irc: 417
prpl-oscar: 2343
on_write
my $channel = $contact->channel(
{
on_write => sub {
my ($c, $message) = @_;
$transporter->deliver( $c->contact->name, $message );
},
...
}
);
Send given message to contact - it will be encrypted and split if neccessary. This callback is required.
$c
is a reference to the current channel.
$message
is the message to be sent.
on_read
$channel->read( $raw_message );
my $channel = $contact->channel(
{
on_read => sub {
my ($c, $message) = @_;
print "From: ", $c->contact->name, "\n";
print "To: ", $c->account->name, "\n";
print "Message:\n", $message, "\n";
},
...
}
);
Receives decrypted messages. This callback is required.
$c
is a reference to the current channel.
$message
is the received message.
Note: internal protocol messages are not received by this callback.
on_gone_secure
my $channel = $contact->channel(
{
on_gone_secure => sub {
my ($c) = @_;
print "Channel between ", $c->contact->name , " and ",
$c->account->name, " is now secure\n";
my $fp = $c->contact->active_fingerprint;
unless ( $fp->is_verified ) {
print "Fingerprint ", $fp->hash, " is not verified\n";
}
},
...
}
);
Called when the channel has entered secure state.
$c
is a reference to the current channel.
on_gone_insecure
my $channel = $contact->channel(
{
on_gone_insecure => sub {
my ($c) = @_;
print "Channel between ", $c->contact->name , " and ",
$c->account->name, " is not secure anymore\n";
},
...
}
);
Called when the channel has left secure state.
$c
is a reference to the current channel.
on_still_secure
my $channel = $contact->channel(
{
on_still_secure => sub {
my ($c) = @_;
print "Channel between ", $c->contact->name , " and ",
$c->account->name, " is still secure\n";
},
...
}
);
Called when the channel has entered secure state using already known D-H keys.
$c
is a reference to the current channel.
on_unverified_fingerprint
my $channel = $contact->channel(
{
on_unverified_fingerprint => sub {
my ($c, $fingerprint_hash, $seen_before) = @_;
print "Unverified fingerprint ", $fingerprint_hash,
" from ", $c->contact->name ,
" to ", $c->account->name,
" is ", ( $seen_before ?
"unrecognised" :
"not authenticated"
), "\n";
},
...
}
);
Called when new fingerprint has been received.
$c
is a reference to the current channel.
$fingerprint_hash
is the human readable hash of the fingerprint.
$seen_before
is a boolean indicating if the fingerprint was seen before.
on_symkey
my $channel = $contact->channel(
{
on_symkey => sub {
my ($c, $symkey, $use, $use_for) = @_;
print "Received symmetric key for ", $use_for, " (", $use,") ",
" from ", $c->contact->name , " to ", $c->account->name,
":\n", unpack("H*", $symkey), "\n";
encrypt_file( $symkey );
},
...
}
);
Called when received symmetric key from our contact. Example use is as password to archive file.
$c
is a reference to the current channel.
$symkey
is the symmetric key.
$use
is the numeric code of the requested use (use numbers > 1).
$use_for
is the use specific data.
on_timer
my $channel = $contact->channel(
{
on_timer => sub {
my ($c, $interval) = @_;
undef $ping_timer;
if ( $interval > 0 ) {
$ping_timer = AE::timer 0, $interval, sub {
$c->ping();
};
}
},
...
}
);
When called, turn off any existing periodic timer.
Additionally, if interval > 0, set a new periodic timer to go off every $interval
seconds. When that timer fires, you must call "ping" method.
The timing does not have to be exact; this timer is used to provide forward secrecy by cleaning up stale private state that may otherwise stick around in memory. Note that the on_timer
callback may be invoked from "ping" itself, possibly to indicate that $interval
== 0 (that is, that there's no more periodic work to be done at this time).
If you set this callback is not provided, then you must ensure that your application calls "ping" every minute. The advantage of implementing the on_timer
callback is that the timer can be turned on by the library only when it's needed.
It is not a problem (except for a minor performance hit) to call "ping" more often than requested, whether on_timer
is implemented or not.
If you fail to implement the on_timer
callback, and also fail to periodically call "ping", then you open your users to a possible forward secrecy violation: an attacker that compromises the user's computer may be able to decrypt a handful of long-past messages (the first messages of an OTR conversation).
$c
is a reference to the current channel.
$interval
is described above.
on_smp
my $channel = $contact->channel(
{
on_smp => sub {
my ($c, $question) = @_;
print "SMP verification in channel between ", $c->contact->name,
" and ", $c->account->name, "\n";
return handle_smp( $question );
},
...
}
);
Called when received SMP verification. Return the expected answer to confirm your identity.
$c
is a reference to the current channel.
$question
is an optional question/hint.
on_error
my $channel = $contact->channel(
{
on_error => sub {
my ($c, $error_code) = @_;
print "Handling error in channel between ", $c->contact->name,
" and ", $c->account->name, "\n";
handle_error( $c, $error_code );
},
...
}
);
Called when error occured in the channel. See ":error_codes" in Protocol::OTR for possible errors.
$c
is a reference to the current channel.
$error_code
is the numeric code of error.
on_event
my $channel = $contact->channel(
{
on_event => sub {
my ($c, $event_code, $message) = @_;
print "Handling event in channel between ", $c->contact->name,
" and ", $c->account->name, "\n";
handle_event( $c, $event_code, $message );
},
...
}
);
Called when event occured in the channel. See ":event_codes" in Protocol::OTR for possible events.
$c
is a reference to the current channel.
$event_code
is the numeric code of event.
$message
is set only for following events: MSGEVENT_SETUP_ERROR
, MSGEVENT_RCVDMSG_GENERAL_ERR
, MSGEVENT_RCVDMSG_UNENCRYPTED
.
on_smp_event
my $channel = $contact->channel(
{
on_smp_event => sub {
my ($c, $smp_event_code, $progress) = @_;
print "Handling SMP event (progress at ", $progress, "%) ",
"in channel between ", $c->contact->name,
" and ", $c->account->name, "\n";
handle_smp_event( $c, $smp_event_code, $progress );
},
...
}
);
Called when SMP event occured in the channel. See ":smp_event_codes" in Protocol::OTR for possible events.
$c
is a reference to the current channel.
$smp_event_code
is the numeric code of SMP event.
$progress
indicates the overall progress of SMP verification process.
on_before_encrypt
my $channel = $contact->channel(
{
on_before_encrypt => sub {
my ($c, $message) = @_;
return $translator->write( $message );
},
...
}
);
Called immediately before a data message is encrypted.
$c
is a reference to the current channel.
$message
is the message to be sent.
on_after_decrypt
my $channel = $contact->channel(
{
on_after_decrypt => sub {
my ($c, $message) = @_;
return $translator->read( $message );
},
...
}
);
Called immediately after a data message is decrypted.
$c
is a reference to the current channel.
$message
is the received message.
on_is_contact_logged_in
my $channel = $contact->channel(
{
on_is_contact_logged_in => sub {
my ($c) = @_;
return 1;
},
...
}
);
Report whether you think the given user is online. Return 1 if yes, 0 if no, -1 if unkown.
If you return 1, messages such as heartbeats or other notifications may be sent to the user, which could result in "not logged in" errors if you're wrong.
$c
is a reference to the current channel.
SEE ALSO
AUTHOR
Alex J. G. Burzyński <ajgb@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2014 by Alex J. G. Burzyński <ajgb@cpan.org>.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.