319 lines
8.1 KiB
Go
319 lines
8.1 KiB
Go
// (C) 1996-1998 Luigi Rizzo (luigi@iet.unipi.it)
|
|
// 2009-2010 Jack Lloyd (lloyd@randombit.net)
|
|
// 2011 Billy Brumley (billy.brumley@aalto.fi)
|
|
// 2016-2017 Vivint, Inc. (jeff.wendling@vivint.com)
|
|
//
|
|
// Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
|
|
// Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
|
|
// Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the
|
|
// distribution.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
|
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT,
|
|
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
package infectious
|
|
|
|
import "sort"
|
|
|
|
// FEC represents operations performed on a Reed-Solomon-based
|
|
// forward error correction code. Make sure to construct using NewFEC.
|
|
type FEC struct {
|
|
k int
|
|
n int
|
|
enc_matrix []byte
|
|
vand_matrix []byte
|
|
}
|
|
|
|
// NewFEC creates a *FEC using k required pieces and n total pieces.
|
|
// Encoding data with this *FEC will generate n pieces, and decoding
|
|
// data requires k uncorrupted pieces. If during decode more than k pieces
|
|
// exist, corrupted data can be detected and recovered from.
|
|
func NewFEC(k, n int) (*FEC, error) {
|
|
if k <= 0 || n <= 0 || k > 256 || n > 256 || k > n {
|
|
return nil, Error.New("requires 1 <= k <= n <= 256")
|
|
}
|
|
|
|
enc_matrix := make([]byte, n*k)
|
|
temp_matrix := make([]byte, n*k)
|
|
createInvertedVdm(temp_matrix, k)
|
|
|
|
for i := k * k; i < len(temp_matrix); i++ {
|
|
temp_matrix[i] = gf_exp[((i/k)*(i%k))%255]
|
|
}
|
|
|
|
for i := 0; i < k; i++ {
|
|
enc_matrix[i*(k+1)] = 1
|
|
}
|
|
|
|
for row := k * k; row < n*k; row += k {
|
|
for col := 0; col < k; col++ {
|
|
pa := temp_matrix[row:]
|
|
pb := temp_matrix[col:]
|
|
acc := byte(0)
|
|
for i := 0; i < k; i, pa, pb = i+1, pa[1:], pb[k:] {
|
|
acc ^= gf_mul_table[pa[0]][pb[0]]
|
|
}
|
|
enc_matrix[row+col] = acc
|
|
}
|
|
}
|
|
|
|
// vand_matrix has more columns than rows
|
|
// k rows, n columns.
|
|
vand_matrix := make([]byte, k*n)
|
|
vand_matrix[0] = 1
|
|
g := byte(1)
|
|
for row := 0; row < k; row++ {
|
|
a := byte(1)
|
|
for col := 1; col < n; col++ {
|
|
vand_matrix[row*n+col] = a // 2.pow(i * j) FIGURE IT OUT
|
|
a = gf_mul_table[g][a]
|
|
}
|
|
g = gf_mul_table[2][g]
|
|
}
|
|
|
|
return &FEC{
|
|
k: k,
|
|
n: n,
|
|
enc_matrix: enc_matrix,
|
|
vand_matrix: vand_matrix,
|
|
}, nil
|
|
}
|
|
|
|
// Required returns the number of required pieces for reconstruction. This is
|
|
// the k value passed to NewFEC.
|
|
func (f *FEC) Required() int {
|
|
return f.k
|
|
}
|
|
|
|
// Total returns the number of total pieces that will be generated during
|
|
// encoding. This is the n value passed to NewFEC.
|
|
func (f *FEC) Total() int {
|
|
return f.n
|
|
}
|
|
|
|
// Encode will take input data and encode to the total number of pieces n this
|
|
// *FEC is configured for. It will call the callback output n times.
|
|
//
|
|
// The input data must be a multiple of the required number of pieces k.
|
|
// Padding to this multiple is up to the caller.
|
|
//
|
|
// Note that the byte slices in Shares passed to output may be reused when
|
|
// output returns.
|
|
func (f *FEC) Encode(input []byte, output func(Share)) error {
|
|
size := len(input)
|
|
|
|
k := f.k
|
|
n := f.n
|
|
enc_matrix := f.enc_matrix
|
|
|
|
if size%k != 0 {
|
|
return Error.New("input length must be a multiple of %d", k)
|
|
}
|
|
|
|
block_size := size / k
|
|
|
|
for i := 0; i < k; i++ {
|
|
output(Share{
|
|
Number: i,
|
|
Data: input[i*block_size : i*block_size+block_size]})
|
|
}
|
|
|
|
fec_buf := make([]byte, block_size)
|
|
for i := k; i < n; i++ {
|
|
for j := range fec_buf {
|
|
fec_buf[j] = 0
|
|
}
|
|
|
|
for j := 0; j < k; j++ {
|
|
addmul(fec_buf, input[j*block_size:j*block_size+block_size],
|
|
enc_matrix[i*k+j])
|
|
}
|
|
|
|
output(Share{
|
|
Number: i,
|
|
Data: fec_buf})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// EncodeSingle will take input data and encode it to output only for the num
|
|
// piece.
|
|
//
|
|
// The input data must be a multiple of the required number of pieces k.
|
|
// Padding to this multiple is up to the caller.
|
|
//
|
|
// The output must be exactly len(input) / k bytes.
|
|
//
|
|
// The num must be 0 <= num < n.
|
|
func (f *FEC) EncodeSingle(input, output []byte, num int) error {
|
|
size := len(input)
|
|
|
|
k := f.k
|
|
n := f.n
|
|
enc_matrix := f.enc_matrix
|
|
|
|
if num < 0 {
|
|
return Error.New("num must be non-negative")
|
|
}
|
|
|
|
if num >= n {
|
|
return Error.New("num must be less than %d", n)
|
|
}
|
|
|
|
if size%k != 0 {
|
|
return Error.New("input length must be a multiple of %d", k)
|
|
}
|
|
|
|
block_size := size / k
|
|
|
|
if len(output) != block_size {
|
|
return Error.New("output length must be %d", block_size)
|
|
}
|
|
|
|
if num < k {
|
|
copy(output, input[num*block_size:])
|
|
return nil
|
|
}
|
|
|
|
for i := range output {
|
|
output[i] = 0
|
|
}
|
|
|
|
for i := 0; i < k; i++ {
|
|
addmul(output, input[i*block_size:i*block_size+block_size],
|
|
enc_matrix[num*k+i])
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// A Share represents a piece of the FEC-encoded data.
|
|
// Both fields are required.
|
|
type Share struct {
|
|
Number int
|
|
Data []byte
|
|
}
|
|
|
|
// DeepCopy makes getting a deep copy of a Share easier. It will return an
|
|
// identical Share that uses all new memory locations.
|
|
func (s *Share) DeepCopy() (c Share) {
|
|
c.Number = s.Number
|
|
c.Data = append([]byte(nil), s.Data...)
|
|
return c
|
|
}
|
|
|
|
type byNumber []Share
|
|
|
|
func (b byNumber) Len() int { return len(b) }
|
|
func (b byNumber) Less(i int, j int) bool { return b[i].Number < b[j].Number }
|
|
func (b byNumber) Swap(i int, j int) { b[i], b[j] = b[j], b[i] }
|
|
|
|
// Rebuild will take a list of corrected shares (pieces) and a callback output.
|
|
// output will be called k times ((*FEC).Required() times) with 1/k of the
|
|
// original data each time and the index of that data piece.
|
|
// Decode is usually preferred.
|
|
//
|
|
// Note that the data is not necessarily sent to output ordered by the piece
|
|
// number.
|
|
//
|
|
// Note that the byte slices in Shares passed to output may be reused when
|
|
// output returns.
|
|
//
|
|
// Rebuild assumes that you have already called Correct or did not need to.
|
|
func (f *FEC) Rebuild(shares []Share, output func(Share)) error {
|
|
k := f.k
|
|
n := f.n
|
|
enc_matrix := f.enc_matrix
|
|
|
|
if len(shares) < k {
|
|
return NotEnoughShares.New("")
|
|
}
|
|
|
|
share_size := len(shares[0].Data)
|
|
sort.Sort(byNumber(shares))
|
|
|
|
m_dec := make([]byte, k*k)
|
|
indexes := make([]int, k)
|
|
sharesv := make([][]byte, k)
|
|
|
|
shares_b_iter := 0
|
|
shares_e_iter := len(shares) - 1
|
|
|
|
for i := 0; i < k; i++ {
|
|
var share_id int
|
|
var share_data []byte
|
|
|
|
if share := shares[shares_b_iter]; share.Number == i {
|
|
share_id = share.Number
|
|
share_data = share.Data
|
|
shares_b_iter++
|
|
} else {
|
|
share := shares[shares_e_iter]
|
|
share_id = share.Number
|
|
share_data = share.Data
|
|
shares_e_iter--
|
|
}
|
|
|
|
if share_id >= n {
|
|
return Error.New("invalid share id: %d", share_id)
|
|
}
|
|
|
|
if share_id < k {
|
|
m_dec[i*(k+1)] = 1
|
|
if output != nil {
|
|
output(Share{
|
|
Number: share_id,
|
|
Data: share_data})
|
|
}
|
|
} else {
|
|
copy(m_dec[i*k:i*k+k], enc_matrix[share_id*k:])
|
|
}
|
|
|
|
sharesv[i] = share_data
|
|
indexes[i] = share_id
|
|
}
|
|
|
|
if err := invertMatrix(m_dec, k); err != nil {
|
|
return err
|
|
}
|
|
|
|
buf := make([]byte, share_size)
|
|
for i := 0; i < len(indexes); i++ {
|
|
if indexes[i] >= k {
|
|
for j := range buf {
|
|
buf[j] = 0
|
|
}
|
|
|
|
for col := 0; col < k; col++ {
|
|
addmul(buf, sharesv[col], m_dec[i*k+col])
|
|
}
|
|
|
|
if output != nil {
|
|
output(Share{
|
|
Number: i,
|
|
Data: buf})
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|