Crypt-IDA
view release on metacpan or search on metacpan
lib/Crypt/IDA/Algorithm.pm view on Meta::CPAN
package Crypt::IDA::Algorithm;
use strict;
use warnings;
use Math::FastGF2::Matrix;
use Crypt::IDA;
use Crypt::IDA::SlidingWindow;
use Class::Tiny qw/sw/,
{
# I'm getting rid of 'n' because:
# * It's meaningless when combining
# * It's either implied or overridden when splitting
k => undef, w => 1,
mode => undef, bufsize => 16384,
inorder => 0, outorder => 0, # passed to getvals_str/setvals_str
# Simplify transform/key specification. Either provide a transform
# matrix or a key with optional sharelist. Don't support sharelist
# with xform matrix or auto-generating a key.
xform => undef,
key => undef, # [@xvals, @yvals]
sharelist => undef,
# add some callbacks below...
};
sub BUILD {
my ($self, $args) = @_;
for my $req ( qw(k w mode bufsize inorder outorder) ) {
die "$req attribute required" unless defined $self->$req;
}
die "Bad mode!" unless $self->{mode} =~ /^(split|combine)$/;
for my $plus ( qw(k w bufsize) ) {
die "$plus attribute strictly positive" unless $self->$plus > 0;
}
for my $ints ( qw(k w bufsize) ) {
die "$ints attribute must be a whole number"
unless int($self->$ints) == $self->$ints;
}
die "w must be 1, 2 or 4" unless $self->w =~ /^[124]$/;
# bump bufsize until it's a multiple of k (no: it's # of cols!)
# $self->{bufsize}++ while $self->{bufsize} % $self->k;
# I have eliminated n being passed in as a parameter, but we need
# to calculate it (or its apparent value) from the other
# parameters. Why? So we know what the sizes of the input/output
# matrices should be. I'll call it 'xform_rows', though.
my $xform_rows = undef;
my $k = $self->k;
# The two ways of specifying the transform matrix are mutually
# exclusive
if (defined($self->{xform})) {
die "Cannot use xform with key or sharelist"
if defined($self->{key}) or defined($self->{sharelist});
$xform_rows = $self->{xform}->ROWS; # apparent value
} else {
# The new_cauchy and new_inverse_cauchy methods in
# Math::FastGF2::Matrix can do a lot of parameter checking for
# us. However, the parameter lists are slightly different for
# split and combine, and we also want to set a default...
#
# It's probably better to check as much as we can here so that
# the caller gets error messages higher up the call stack
# (with parameter names/error messages that make more sense).
if (defined $self->{key}) {
# break up list into yvals, xvals
my @key = @{$self->{key}};
my @yvals = splice @key, -$k;
# apply sharelist, moving to new @xvals list
my @xvals;
if (defined $self->{sharelist}) {
push @xvals, $key[$_] foreach @{$self->{sharelist}};
} else {
@xvals = @key;
# $self->{sharelist} = [ 0 .. $xvals - 1 ];
}
$xform_rows = scalar(@xvals);
# error check and make all matrices
if ($self->mode eq 'split') {
lib/Crypt/IDA/Algorithm.pm view on Meta::CPAN
Crypt::IDA::Algorithm - Expose methods useful for writing custom IDA loops
=head1 SYNOPSIS
use Crypt::IDA::Algorithm;
use Digest::HMAC_SHA1 qw/hmac_sha1_hex/;
# Make cryptographically secure ticket for entry to a party
my $secret = 'Not just any Tom, Dick and Harry';
my $ticket = 'Admit Tom, Dick /and/ Harry to the party together';
my $signed = "$ticket:" . hmac_sha1_hex($ticket,$secret);
# Algorithm works on full matrix columns, so must pad the message
$signed .= "\0" while length($signed) % 3;
# Turn the signed ticket into three shares
my $s = Crypt::IDA::Algorithm->splitter(k=>3, key=>[1..6]);
$s->fill_stream($signed);
$s->split_stream;
my @tickets = map { $s->empty_substream($_) } (0..2);
# At the party, Tom, Dick and Harry present shares to be combined
my $c = Crypt::IDA::Algorithm->combiner(k=>3, key=>[1..6],
sharelist=>[0..2]);
$c->fill_substream($_, $tickets[$_]) foreach (0..2);
$c->combine_streams;
my $got = $c->empty_stream;
# Check the recovered ticket
$got =~ /^(.*):(.*)\0*$/;
my ($msg, $sig) = ($1,$2);
die "Fake!\n" unless $sig eq hmac_sha1_hex($msg,$secret);
print "Welcome! $msg!\n";
=head1 DESCRIPTION
This module is a rewrite of the original C<ida_split> and
C<ida_combine> methods provided in C<Crypt::IDA>. It provides a
pared-down, simplified interface intended to make it easier to
integrate with an external event loop. It does this by:
=over
=item * Decoupling processing from I/O (caller handles I/O and passes
data to this module as strings)
=item * Eliminating the inner loop (caller decides when/how to loop, if needed)
=item * Allowing caller to register callback hooks to become notified
when something "interesting" happens within the code (eg, new input
became available or space became available in the output buffer)
=back
=head2 NOTICE
This code has been tested to make sure that it replicates the
behaviour of the original C<Crypt::IDA> implementation. However, I
have not yet implemented any callback functionality that would make it
easier to integrate with an external event loop. I will add callbacks
in a later release.
=head1 CODE ORGANISATION
The internal organisation of the code has been improved. The main
C<Crypt::IDA> loops (C<ida_split> and C<ida_combine>) both call a
generic internal C<ida_process_streams> loop. It has very complicated
logic to enable it to handle different matrix layouts and circular
buffer reads/writes, as well as dealing with partial matrix columns.
By contrast, this new code:
=over
=item * always deals with full matrix columns
=item * abstracts away the circular buffering into a new, more generic
"Sliding Window" class (C<Crypt::IDA::SlidingWindow>, accessible via
the C<sw> accessor)
=item * treats split and combine separately, providing different
method interfaces appropriate to each
=back
This new code also avoids using `name => value` style
parameter-passing interface, apart from in the constructor methods.
Although my main design goal for this class was to make it work better
with external event loops, the cleaner interface, with less
boilerplate for setting up fillers/emptiers, means that it might be
more comfortable to use in general. Even if you're not using an event
loop.
=head1 GENERAL OPERATION
By way of recap, the IDA split and combine operations are a set of
matrix operations:
transform x input -> output
The constructor will create a set of input and output matrix buffers
as follows:
+----------+>---------+ +>---------+----------+
v | Output | | Input v v
| Input +>---------+ +>---------+ Output |
| = | = | | = | = |
| COLWISE +>---------+ +>---------+ COLWISE |
| | ROWWISE | | ROWWISE | |
+----------+>---------+ +>---------+----------+
| : | | : |
SPLIT COMBINE
In both cases, one full column of input produces one full column of
output. Matrix columns are written to and read circularly, with checks
made to prevent overruns and underruns. To simplify this processing,
the input and output buffers are created with the same number of
columns (the "window size") in each.
lib/Crypt/IDA/Algorithm.pm view on Meta::CPAN
=over
=item * from the number of rows in a supplied 'xform' matrix
=item * from the number of C<@xvals> in the key, after the k C<@yvals>
are accounted for
=item * from the number of elements in 'sharelist' (if both 'key' and
'sharelist' were provided)
=back
=head2 Crypt::IDA::Algorithm->combiner()
The full list of options available when creating a new combiner is as follows:
my $combiner = Crypt::IDA::Algorithm->combiner(
# Required:
k => 4, # Quorum value (4 shares needed to combine)
# Supply either a matrix ...
xform => new_inverse_cauchy(...),
# ... or a key (sharelist required and must have k elements)
key => [ @xvals, @yvals ], # scalar (@yvals) == k
sharelist => [0..3], # use xvals[0..3] to create inverse xform
# defaults provided:
w => 1, # field width == 1 byte
bufsize => 16384, # columns in in/out matrices
inorder => 0, # no byte-swapping ...
outorder => 0, # ie, native byte order
);
=head1 CALLBACKS
None currently implemented in this class, but see
C<Crypt::IDA::SlidingWindow>.
=head1 INTEGRATION WITH EVENT LOOPS
As this package stands, there's nothing actually stopping it from
being used within an event loop. If the input and output is over
network sockets, for example, all the major event loops have features
for handling this in a non-blocking way. Most will have an equivalent
of an "on_read" callback that can be used to receive new data, which
can then be passed to this class for transformation, then the output
can be sent to another non-blocking socket (or sockets). So long as
the output is non-blocking, then the IDA output matrix can always be
flushed, so split/combine operations only block for as long as the
calculations take.
While I imagine that the above way of calling this class will be
typical, I also suspect that other people might have their own idea of
how this code should be called (or encapsulated) within their own
particular event framework. As with Perl, there's definitely more than
one way to do event-driven programming.
It seems that the easiest way to support arbitrary event loops is by
providing callbacks for when various "interesting" things happen
within the algorithm, such as an input matrix becoming full, or space
becoming available within the output matrix. This kind of approach is a
natural fit, since it's the dominant style of event-driven programming.
However, without untangling what the most common use cases are, it's
not really possible to determine in advance exactly I<which> callbacks
I should implement. I don't want to add unnecessary complexity or a
bunch of incompatible callbacks. As a result, I'm not going to tackle
that problem in this release.
As the code stands, there is I<partial> support for using callbacks.
The C<SlidingWindow> object (accessed via C<{sw}>) can be set up to
trigger a C<cb_write_bundle> or C<cb_read_bundle> callback when the
slowest stream in a substream advances.
=head1 AUTHOR
Declan Malone, E<lt>idablack@sourceforge.netE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2019 Declan Malone
This package is free software; you can redistribute it and/or modify
it under the terms of version 2 (or, at your discretion, any later
version) of the "GNU General Public License" ("GPL").
Please refer to the file "GNU_GPL.txt" in this distribution for
details.
=head1 DISCLAIMER
This package is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
=cut
( run in 0.536 second using v1.01-cache-2.11-cpan-39bf76dae61 )