github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfscrypto/pad.go (about)

     1  // Copyright 2019 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package kbfscrypto
     6  
     7  import (
     8  	"encoding/binary"
     9  	"io"
    10  
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  const (
    15  	padPrefixSize = 4
    16  	minBlockSize  = 256
    17  )
    18  
    19  // powerOfTwoEqualOrGreater returns smallest power of 2 greater than or equal
    20  // to the input n.
    21  // https://en.wikipedia.org/wiki/Power_of_two#Algorithm_to_round_up_to_power_of_two
    22  func powerOfTwoEqualOrGreater(n int) int {
    23  	if n <= minBlockSize {
    24  		return minBlockSize
    25  	}
    26  	if n&(n-1) == 0 {
    27  		// if n is already power of 2, return it
    28  		return n
    29  	}
    30  
    31  	n--
    32  	n |= (n >> 1)
    33  	n |= (n >> 2)
    34  	n |= (n >> 4)
    35  	n |= (n >> 8)
    36  	n |= (n >> 16)
    37  	n |= (n >> 16 >> 16) // make it work with 64 bit int; no effect on 32bit.
    38  	n++
    39  
    40  	return n
    41  }
    42  
    43  // PadBlock adds zero padding to an encoded block.
    44  func PadBlock(block []byte) ([]byte, error) {
    45  	totalLen := powerOfTwoEqualOrGreater(len(block))
    46  
    47  	buf := make([]byte, padPrefixSize+totalLen)
    48  	binary.LittleEndian.PutUint32(buf, uint32(len(block)))
    49  
    50  	copy(buf[padPrefixSize:], block)
    51  	return buf, nil
    52  }
    53  
    54  // DepadBlock extracts the actual block data from a padded block.
    55  func DepadBlock(paddedBlock []byte) ([]byte, error) {
    56  	totalLen := len(paddedBlock)
    57  	if totalLen < padPrefixSize {
    58  		return nil, errors.WithStack(io.ErrUnexpectedEOF)
    59  	}
    60  
    61  	blockLen := binary.LittleEndian.Uint32(paddedBlock)
    62  	blockEndPos := int(blockLen + padPrefixSize)
    63  
    64  	if totalLen < blockEndPos {
    65  		return nil, errors.WithStack(
    66  			PaddedBlockReadError{
    67  				ActualLen:   totalLen,
    68  				ExpectedLen: blockEndPos,
    69  			})
    70  	}
    71  	return paddedBlock[padPrefixSize:blockEndPos], nil
    72  }