containerd/vendor/github.com/Microsoft/hcsshim/ext4/dmverity/dmverity.go
Daniel Canter 46b152f81b vendor: Bump hcsshim to 0.9.0
This change bumps hcsshim to 0.9.0. Main thing this tag contains is support for
Kubernetes Host Process containers
See: https://kubernetes.io/docs/tasks/configure-pod-container/create-hostprocess-pod/

Signed-off-by: Daniel Canter <dcanter@microsoft.com>
2021-10-07 21:00:35 -07:00

199 lines
5.5 KiB
Go

package dmverity
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"fmt"
"io"
"os"
"github.com/pkg/errors"
"github.com/Microsoft/hcsshim/ext4/internal/compactext4"
)
const (
blockSize = compactext4.BlockSize
// RecommendedVHDSizeGB is the recommended size in GB for VHDs, which is not a hard limit.
RecommendedVHDSizeGB = 128 * 1024 * 1024 * 1024
)
var salt = bytes.Repeat([]byte{0}, 32)
var (
ErrSuperBlockReadFailure = errors.New("failed to read dm-verity super block")
ErrSuperBlockParseFailure = errors.New("failed to parse dm-verity super block")
ErrRootHashReadFailure = errors.New("failed to read dm-verity root hash")
)
type dmveritySuperblock struct {
/* (0) "verity\0\0" */
Signature [8]byte
/* (8) superblock version, 1 */
Version uint32
/* (12) 0 - Chrome OS, 1 - normal */
HashType uint32
/* (16) UUID of hash device */
UUID [16]byte
/* (32) Name of the hash algorithm (e.g., sha256) */
Algorithm [32]byte
/* (64) The data block size in bytes */
DataBlockSize uint32
/* (68) The hash block size in bytes */
HashBlockSize uint32
/* (72) The number of data blocks */
DataBlocks uint64
/* (80) Size of the salt */
SaltSize uint16
/* (82) Padding */
_ [6]byte
/* (88) The salt */
Salt [256]byte
/* (344) Padding */
_ [168]byte
}
// VerityInfo is minimal exported version of dmveritySuperblock
type VerityInfo struct {
// Offset in blocks on hash device
HashOffsetInBlocks int64
// Set to true, when dm-verity super block is also written on the hash device
SuperBlock bool
RootDigest string
Salt string
Algorithm string
DataBlockSize uint32
HashBlockSize uint32
DataBlocks uint64
Version uint32
}
// MerkleTree constructs dm-verity hash-tree for a given byte array with a fixed salt (0-byte) and algorithm (sha256).
func MerkleTree(data []byte) ([]byte, error) {
layers := make([][]byte, 0)
currentLevel := bytes.NewBuffer(data)
for currentLevel.Len() != blockSize {
blocks := currentLevel.Len() / blockSize
nextLevel := bytes.NewBuffer(make([]byte, 0))
for i := 0; i < blocks; i++ {
block := make([]byte, blockSize)
_, err := currentLevel.Read(block)
if err != nil {
return nil, errors.Wrap(err, "failed to read data block")
}
h := hash2(salt, block)
nextLevel.Write(h)
}
padding := bytes.Repeat([]byte{0}, blockSize-(nextLevel.Len()%blockSize))
nextLevel.Write(padding)
currentLevel = nextLevel
layers = append(layers, currentLevel.Bytes())
}
var tree = bytes.NewBuffer(make([]byte, 0))
for i := len(layers) - 1; i >= 0; i-- {
_, err := tree.Write(layers[i])
if err != nil {
return nil, errors.Wrap(err, "failed to write merkle tree")
}
}
return tree.Bytes(), nil
}
// RootHash computes root hash of dm-verity hash-tree
func RootHash(tree []byte) []byte {
return hash2(salt, tree[:blockSize])
}
// NewDMVeritySuperblock returns a dm-verity superblock for a device with a given size, salt, algorithm and versions are
// fixed.
func NewDMVeritySuperblock(size uint64) *dmveritySuperblock {
superblock := &dmveritySuperblock{
Version: 1,
HashType: 1,
UUID: generateUUID(),
DataBlockSize: blockSize,
HashBlockSize: blockSize,
DataBlocks: size / blockSize,
SaltSize: uint16(len(salt)),
}
copy(superblock.Signature[:], "verity")
copy(superblock.Algorithm[:], "sha256")
copy(superblock.Salt[:], salt)
return superblock
}
func hash2(a, b []byte) []byte {
h := sha256.New()
h.Write(append(a, b...))
return h.Sum(nil)
}
func generateUUID() [16]byte {
res := [16]byte{}
if _, err := rand.Read(res[:]); err != nil {
panic(err)
}
return res
}
// ReadDMVerityInfo extracts dm-verity super block information and merkle tree root hash
func ReadDMVerityInfo(vhdPath string, offsetInBytes int64) (*VerityInfo, error) {
vhd, err := os.OpenFile(vhdPath, os.O_RDONLY, 0)
if err != nil {
return nil, err
}
defer vhd.Close()
// Skip the ext4 data to get to dm-verity super block
if s, err := vhd.Seek(offsetInBytes, io.SeekStart); err != nil || s != offsetInBytes {
if err != nil {
return nil, errors.Wrap(err, "failed to seek dm-verity super block")
}
return nil, errors.Errorf("failed to seek dm-verity super block: expected bytes=%d, actual=%d", offsetInBytes, s)
}
block := make([]byte, blockSize)
if s, err := vhd.Read(block); err != nil || s != blockSize {
if err != nil {
return nil, errors.Wrapf(ErrSuperBlockReadFailure, "%s", err)
}
return nil, errors.Wrapf(ErrSuperBlockReadFailure, "unexpected bytes read: expected=%d, actual=%d", blockSize, s)
}
dmvSB := &dmveritySuperblock{}
b := bytes.NewBuffer(block)
if err := binary.Read(b, binary.LittleEndian, dmvSB); err != nil {
return nil, errors.Wrapf(ErrSuperBlockParseFailure, "%s", err)
}
// read the merkle tree root
if s, err := vhd.Read(block); err != nil || s != blockSize {
if err != nil {
return nil, errors.Wrapf(ErrRootHashReadFailure, "%s", err)
}
return nil, errors.Wrapf(ErrRootHashReadFailure, "unexpected bytes read: expected=%d, actual=%d", blockSize, s)
}
rootHash := hash2(dmvSB.Salt[:dmvSB.SaltSize], block)
return &VerityInfo{
RootDigest: fmt.Sprintf("%x", rootHash),
Algorithm: string(bytes.Trim(dmvSB.Algorithm[:], "\x00")),
Salt: fmt.Sprintf("%x", dmvSB.Salt[:dmvSB.SaltSize]),
HashOffsetInBlocks: int64(dmvSB.DataBlocks),
SuperBlock: true,
DataBlocks: dmvSB.DataBlocks,
DataBlockSize: dmvSB.DataBlockSize,
HashBlockSize: blockSize,
Version: dmvSB.Version,
}, nil
}