This repository has been archived by the owner on Apr 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
gctx.go
135 lines (117 loc) · 3.53 KB
/
gctx.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
Copyright Edgeless Systems GmbH
Copyright 2022- IBM Inc. All rights reserved
SPDX-License-Identifier: Apache-2.0
*/
/*
- Guest Context (GCTX).
- VM Save Area (VMSA).
- Reverse Map Table (RMP).
- Guest Physical Address (GPA).
*/
package gctx
import (
"bytes"
"crypto/sha512"
"encoding/binary"
"errors"
"fmt"
)
const (
VMSA_GPA = 0xFFFFFFFFF000
LD_SIZE = sha512.Size384
PAGE_SIZE = 4096
)
// GCTX represents a SNP Guest Context.
// VMSA page is recorded in the RMP table with GPA (u64)(-1).
// However, the address is page-aligned, and also all the bits above 51 are cleared.
type GCTX struct {
// ld is the launch digest of the guest.
ld []byte
}
func New(seed []byte) *GCTX {
if seed == nil {
seed = bytes.Repeat([]byte{0x00}, LD_SIZE)
}
return &GCTX{
ld: seed,
}
}
// LD returns the launch digest of the guest.
func (g *GCTX) LD() []byte {
return g.ld
}
// update extends the current launch digest with the hash of a page.
// The hash also includes the page type, GPA, and permissions.
func (g *GCTX) update(pageType byte, gpa uint64, contents []byte) error {
if len(contents) != LD_SIZE {
return errors.New("invalid content size")
}
pageInfoLen := 0x70
isImi := 0
vmpl3Perms := 0
vmpl2Perms := 0
vmpl1Perms := 0
// SNP spec 8.17.2 Table 67 Layout of the PAGE_INFO structure
pageInfo := append(g.ld, contents...)
pageInfo = binary.LittleEndian.AppendUint16(pageInfo, uint16(pageInfoLen))
pageInfo = append(pageInfo, pageType, byte(isImi), byte(vmpl3Perms), byte(vmpl2Perms), byte(vmpl1Perms), byte(0))
pageInfo = binary.LittleEndian.AppendUint64(pageInfo, gpa)
if len(pageInfo) != pageInfoLen {
return errors.New("invalid page info length")
}
// Update the launch digest
h := sha512.New384()
h.Write(pageInfo)
g.ld = h.Sum(nil)
return nil
}
func (g *GCTX) UpdateVmsaPage(data []byte) error {
if len(data) != PAGE_SIZE {
return errors.New("invalid data length")
}
h := sha512.New384()
h.Write(data)
hash := h.Sum(nil)
return g.update(0x02, VMSA_GPA, hash)
}
// UpdateNormalPages extends the current launch digest with the hash of data.
// The hash is generated page by page. Pagetype is set to 0x01.
func (g *GCTX) UpdateNormalPages(startGpa uint64, data []byte) error {
if len(data)%PAGE_SIZE != 0 {
return errors.New("data length should be a multiple of 4096")
}
offset := 0
for offset < len(data) {
pageData := data[offset : offset+PAGE_SIZE]
sha384hash := sha512.Sum384(pageData)
err := g.update(0x01, startGpa+uint64(offset), sha384hash[:])
if err != nil {
return fmt.Errorf("updating page %d: %w", offset/PAGE_SIZE, err)
}
offset += PAGE_SIZE
}
return nil
}
// UpdateZeroPages extends the current launch digest with the hash of a page containing only zeros. Pagetype is set to 0x03.
func (g *GCTX) UpdateZeroPages(gpa uint64, lengthBytes int) error {
if lengthBytes%PAGE_SIZE != 0 {
return errors.New("invalid length")
}
offset := 0
for offset < lengthBytes {
if err := g.update(0x03, gpa+uint64(offset), bytes.Repeat([]byte{0x00}, LD_SIZE)); err != nil {
return err
}
offset += PAGE_SIZE
}
return nil
}
// UpdateSecretsPage extends the current launch digest with the hash of a page containing only zeros. Pagetype is set to 0x05.
func (g *GCTX) UpdateSecretsPage(gpa uint64) error {
return g.update(0x05, gpa, bytes.Repeat([]byte{0x00}, LD_SIZE))
}
// UpdateSecretsPage extends the current launch digest with the hash of a page containing only zeros. Pagetype is set to 0x06.
func (g *GCTX) UpdateCpuidPage(gpa uint64) error {
return g.update(0x06, gpa, bytes.Repeat([]byte{0x00}, LD_SIZE))
}