Skip to content

Commit

Permalink
Add support for OVMF hashes
Browse files Browse the repository at this point in the history
Some times we may not want to have to keep the full OVMF binary handy
when we try to validate a measurement. Most of the data we extract out
of the OVMF binary is static, except for its actual hashes.

So let's introduce a new mode that generates a precalculated launch
digest that contains the SNP ld hash at the point where we fully
ingested OVMF's contents:

  $ sev-snp-measure --mode ovmf-hash --ovmf OVMF.fd
  cab7e[...]

In addition, add a new optional parameter that a user can then use to
consume the hash instead of recalculating it from their OVMF binary at
hand:

  $ sev-snp-measure --mode snp --vcpus=1 --vcpu-type=EPYC-v4 \
                    --ovmf=OVMF.fd.old --ovmf-hash cab7e[...]
  d5269[...]

With this in place, we no longer need to copy full OVMF binaries around.
Instead, for almost all OVMF binaries in existence today, we can merely
share their hash values and are still able to validate the measurement
correctness.

Signed-off-by: Alexander Graf <graf@amazon.com>
  • Loading branch information
agraf committed Mar 24, 2023
1 parent 63ae739 commit 47e03d4
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 8 deletions.
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@ Clone the Github repo and run the script directly from the local directory:

```
$ sev-snp-measure --help
usage: sev-snp-measure [-h] [--version] [-v] --mode {sev,seves,snp} [--vcpus N]
usage: sev-snp-measure [-h] [--version] [-v] --mode {sev,seves,snp,ovmf-hash} [--vcpus N]
[--vcpu-type CPUTYPE] [--vcpu-sig VALUE] [--vcpu-family FAMILY]
[--vcpu-model MODEL] [--vcpu-stepping STEPPING] --ovmf PATH [--kernel PATH]
[--initrd PATH] [--append CMDLINE] [--output-format {hex,base64}]
[--ovmf-hash HASH]
Calculate AMD SEV/SEV-ES/SEV-SNP guest launch measurement
Expand All @@ -56,6 +57,7 @@ optional arguments:
--append CMDLINE Kernel command line to calculate hash from (use with --kernel)
--output-format {hex,base64}
Measurement output format
--ovmf-hash HASH Precalculated hash of the OVMF binary
```

For example:
Expand Down Expand Up @@ -93,6 +95,19 @@ example, the following 3 invocations are identical:
2. `sev-snp-measure --vcpu-sig=0x800f12 ...`
3. `sev-snp-measure --vcpu-family=23 --vcpu-model=1 --vcpu-stepping=2 ...`

## Precalculated OVMF hashes

In situations where only minor OVMF changes happen, you may not want to copy the full OVMF binary to the validation system. In these situations, you can use an OVMF hash.
To generate a hash, use the `--gen-ovmf-hash` parameter:

$ sev-snp-measure --mode ovmf-hash --ovmf OVMF.fd
cab7e085874b3acfdbe2d96dcaa3125111f00c35c6fc9708464c2ae74bfdb048a198cb9a9ccae0b3e5e1a33f5f249819

On a different machine that only has access to an older but compatible OVMF binary, you can then ingest the hash again to generate a full measurement:

$ sev-snp-measure --mode snp --vcpus=1 --vcpu-type=EPYC-v4 --ovmf=OVMF.fd.old --ovmf-hash cab7e[...]
d52697c3e056fb8d698d19cc29adfbed5a8ec9170cb9eb63c2ac957d22b4eb647e25780162036d063a0cf418b8830acc

## Related projects

* libvirt tools: [virt-dom-sev-validate](https://gitlab.com/berrange/libvirt/-/blob/lgtm/tools/virt-dom-sev-validate.py),
Expand Down
9 changes: 7 additions & 2 deletions sevsnpmeasure/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def main() -> int:
description='Calculate AMD SEV/SEV-ES/SEV-SNP guest launch measurement')
parser.add_argument('--version', action='version', version=f'%(prog)s {VERSION}')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('--mode', choices=['sev', 'seves', 'snp'], help='Guest mode', required=True)
parser.add_argument('--mode', choices=['sev', 'seves', 'snp', 'ovmf-hash'], help='Guest mode', required=True)
parser.add_argument('--vcpus', metavar='N', type=int, help='Number of guest vcpus', default=None)
parser.add_argument('--vcpu-type', metavar='CPUTYPE', choices=list(vcpu_types.CPU_SIGS.keys()),
help=f"Type of guest vcpu ({', '.join(vcpu_types.CPU_SIGS.keys())})",
Expand All @@ -41,8 +41,13 @@ def main() -> int:
parser.add_argument('--append', metavar='CMDLINE',
help='Kernel command line to calculate hash from (use with --kernel)')
parser.add_argument('--output-format', choices=['hex', 'base64'], help='Measurement output format', default='hex')
parser.add_argument('--ovmf-hash', metavar='HASH', help='Precalculated hash of the OVMF binary')
args = parser.parse_args()

if args.mode == 'ovmf-hash':
print(guest.calc_ovmf_hash(args.ovmf).hex())
return

if args.mode != 'sev' and args.vcpus is None:
parser.error(f"missing --vcpus N in guest mode '{args.mode}'")

Expand All @@ -58,7 +63,7 @@ def main() -> int:
parser.error(f"missing --vcpu-type or --vcpu-sig or --vcpu-family in guest mode '{args.mode}'")

sev_mode = SevMode.from_str(args.mode)
ld = guest.calc_launch_digest(sev_mode, args.vcpus, vcpu_sig, args.ovmf, args.kernel, args.initrd, args.append)
ld = guest.calc_launch_digest(sev_mode, args.vcpus, vcpu_sig, args.ovmf, args.kernel, args.initrd, args.append, args.ovmf_hash)

if args.output_format == "hex":
measurement = ld.hex()
Expand Down
4 changes: 2 additions & 2 deletions sevsnpmeasure/gctx.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class GCTX(object):
# 51 are cleared.
VMSA_GPA = 0xFFFFFFFFF000

def __init__(self):
self._ld = ZEROS
def __init__(self, seed: bytes = ZEROS):
self._ld = seed

def ld(self) -> bytes:
return self._ld
Expand Down
21 changes: 18 additions & 3 deletions sevsnpmeasure/guest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@


def calc_launch_digest(mode: SevMode, vcpus: int, vcpu_sig: int, ovmf_file: str,
kernel: str, initrd: str, append: str) -> bytes:
kernel: str, initrd: str, append: str, ovmf_hash_str: str = None) -> bytes:
if mode == SevMode.SEV_SNP:
return snp_calc_launch_digest(vcpus, vcpu_sig, ovmf_file, kernel, initrd, append)
return snp_calc_launch_digest(vcpus, vcpu_sig, ovmf_file, kernel, initrd, append, ovmf_hash_str)
elif mode == SevMode.SEV_ES:
return seves_calc_launch_digest(vcpus, vcpu_sig, ovmf_file, kernel, initrd, append)
elif mode == SevMode.SEV:
Expand All @@ -38,11 +38,26 @@ def snp_update_metadata_pages(gctx, ovmf) -> None:
raise ValueError("unknown OVMF metadata section type")


def snp_calc_launch_digest(vcpus: int, vcpu_sig: int, ovmf_file: str, kernel: str, initrd: str, append: str) -> bytes:
def calc_ovmf_hash(ovmf_file: str) -> bytes:
ovmf = OVMF(ovmf_file)

gctx = GCTX()
gctx.update_normal_pages(ovmf.gpa(), ovmf.data())
return gctx.ld()


def snp_calc_launch_digest(vcpus: int, vcpu_sig: int, ovmf_file: str, kernel: str, initrd: str, append: str, ovmf_hash_str: str) -> bytes:

gctx = GCTX()
ovmf = OVMF(ovmf_file)

# Allow users to provide a precalculated OVMF hash.
# Ignores the contents of the OVMF file in front of us.
if ovmf_hash_str:
ovmf_hash = bytearray.fromhex(ovmf_hash_str)
gctx = GCTX(seed = ovmf_hash)
else:
gctx.update_normal_pages(ovmf.gpa(), ovmf.data())

if kernel:
sev_hashes_table_gpa = ovmf.sev_hashes_table_gpa()
Expand Down

0 comments on commit 47e03d4

Please sign in to comment.