shadow_wire.go raw

   1  package crypto
   2  
   3  import (
   4  	"encoding/binary"
   5  	"errors"
   6  )
   7  
   8  // Shadow compression wire format magic bytes.
   9  var shadowMagic = [4]byte{'S', 'D', 'W', '1'}
  10  
  11  // MarshalShadowCompressed encodes a ShadowCompressed into the binary
  12  // wire format.
  13  //
  14  // Layout:
  15  //
  16  //	[4B]   Magic "SDW1"
  17  //	[4B]   OrigLen (uint32 LE)
  18  //	[4B]   TokenCount (uint32 LE)
  19  //	[2B]   EpochDecExp (uint16 LE)
  20  //	[2B]   EpochBinExp (uint16 LE)
  21  //	[4B]   PrimaryLen (uint32 LE)
  22  //	[4B]   ShadowLen (uint32 LE)
  23  //	[4B]   ShadowPayloadLen (uint32 LE)
  24  //	[256B] FreqTable (64 × uint32 LE)
  25  //	[56B]  ContentHash (Hamadryad)
  26  //	[PrimaryLen B] Huffman bitstream
  27  //	[ShadowLen B]  Shadow permutation indices
  28  func MarshalShadowCompressed(sc *ShadowCompressed) ([]byte, error) {
  29  	if sc == nil {
  30  		return nil, errors.New("crypto: nil shadow compressed")
  31  	}
  32  
  33  	headerSize := 4 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 256 + HamBytes // 340
  34  	totalSize := headerSize + len(sc.Primary) + len(sc.Shadow)
  35  	buf := make([]byte, totalSize)
  36  	pos := 0
  37  
  38  	// Magic.
  39  	copy(buf[pos:], shadowMagic[:])
  40  	pos += 4
  41  
  42  	// OrigLen.
  43  	binary.LittleEndian.PutUint32(buf[pos:], uint32(sc.OrigLen))
  44  	pos += 4
  45  
  46  	// TokenCount.
  47  	binary.LittleEndian.PutUint32(buf[pos:], uint32(sc.TokenCount))
  48  	pos += 4
  49  
  50  	// EpochDecExp.
  51  	binary.LittleEndian.PutUint16(buf[pos:], uint16(sc.EpochDec))
  52  	pos += 2
  53  
  54  	// EpochBinExp.
  55  	binary.LittleEndian.PutUint16(buf[pos:], uint16(sc.EpochBin))
  56  	pos += 2
  57  
  58  	// PrimaryLen.
  59  	binary.LittleEndian.PutUint32(buf[pos:], uint32(len(sc.Primary)))
  60  	pos += 4
  61  
  62  	// ShadowLen.
  63  	binary.LittleEndian.PutUint32(buf[pos:], uint32(len(sc.Shadow)))
  64  	pos += 4
  65  
  66  	// ShadowPayloadLen.
  67  	binary.LittleEndian.PutUint32(buf[pos:], uint32(sc.ShadowPayloadLen))
  68  	pos += 4
  69  
  70  	// FreqTable: 64 × uint32.
  71  	for i := range 64 {
  72  		binary.LittleEndian.PutUint32(buf[pos:], sc.FreqTable[i])
  73  		pos += 4
  74  	}
  75  
  76  	// ContentHash.
  77  	copy(buf[pos:], sc.ContentHash[:])
  78  	pos += HamBytes
  79  
  80  	// Primary stream.
  81  	copy(buf[pos:], sc.Primary)
  82  	pos += len(sc.Primary)
  83  
  84  	// Shadow stream.
  85  	copy(buf[pos:], sc.Shadow)
  86  
  87  	return buf, nil
  88  }
  89  
  90  // UnmarshalShadowCompressed decodes a ShadowCompressed from the binary
  91  // wire format.
  92  func UnmarshalShadowCompressed(data []byte) (*ShadowCompressed, error) {
  93  	headerSize := 4 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 256 + HamBytes
  94  	if len(data) < headerSize {
  95  		return nil, errors.New("crypto: shadow compressed data too short")
  96  	}
  97  	pos := 0
  98  
  99  	// Magic.
 100  	if data[0] != 'S' || data[1] != 'D' || data[2] != 'W' || data[3] != '1' {
 101  		return nil, errors.New("crypto: invalid shadow compressed magic")
 102  	}
 103  	pos += 4
 104  
 105  	sc := &ShadowCompressed{}
 106  
 107  	// OrigLen.
 108  	sc.OrigLen = int(binary.LittleEndian.Uint32(data[pos:]))
 109  	pos += 4
 110  
 111  	// TokenCount.
 112  	sc.TokenCount = int(binary.LittleEndian.Uint32(data[pos:]))
 113  	pos += 4
 114  
 115  	// EpochDecExp.
 116  	sc.EpochDec = int(binary.LittleEndian.Uint16(data[pos:]))
 117  	pos += 2
 118  
 119  	// EpochBinExp.
 120  	sc.EpochBin = int(binary.LittleEndian.Uint16(data[pos:]))
 121  	pos += 2
 122  
 123  	// PrimaryLen.
 124  	primaryLen := int(binary.LittleEndian.Uint32(data[pos:]))
 125  	pos += 4
 126  
 127  	// ShadowLen.
 128  	shadowLen := int(binary.LittleEndian.Uint32(data[pos:]))
 129  	pos += 4
 130  
 131  	// ShadowPayloadLen.
 132  	sc.ShadowPayloadLen = int(binary.LittleEndian.Uint32(data[pos:]))
 133  	pos += 4
 134  
 135  	// FreqTable.
 136  	for i := range 64 {
 137  		sc.FreqTable[i] = binary.LittleEndian.Uint32(data[pos:])
 138  		pos += 4
 139  	}
 140  
 141  	// ContentHash.
 142  	copy(sc.ContentHash[:], data[pos:pos+HamBytes])
 143  	pos += HamBytes
 144  
 145  	// Bounds check.
 146  	if pos+primaryLen+shadowLen > len(data) {
 147  		return nil, errors.New("crypto: shadow compressed data truncated")
 148  	}
 149  
 150  	// Primary stream.
 151  	sc.Primary = make([]byte, primaryLen)
 152  	copy(sc.Primary, data[pos:pos+primaryLen])
 153  	pos += primaryLen
 154  
 155  	// Shadow stream.
 156  	sc.Shadow = make([]byte, shadowLen)
 157  	copy(sc.Shadow, data[pos:pos+shadowLen])
 158  
 159  	return sc, nil
 160  }
 161