blob: 5d54ee328910fe773cf81318bac2ddcee40452db [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Lorenz Brunaa6b7342019-12-12 02:55:02 +01002// SPDX-License-Identifier: Apache-2.0
Lorenz Brunaa6b7342019-12-12 02:55:02 +01003
4package tpm
5
Serge Bazanski216fe7b2021-05-21 18:36:16 +02006// This file is adapted from github.com/google/go-tpm/tpm2/credactivation which
7// outputs broken challenges for unknown reasons. They use u16 length-delimited
8// outputs for the challenge blobs which is incorrect. Rather than rewriting
9// the routine, we only applied minimal fixes to it and skip the ECC part of
10// the issue (because we would rather trust the proprietary RSA
11// implementation).
Leopold Schabel8fba0f82020-01-22 18:46:25 +010012//
Serge Bazanski216fe7b2021-05-21 18:36:16 +020013// TODO(lorenz): I'll eventually deal with this upstream, but for now just fix
14// it here (it's not that) much code after all.
15// https://github.com/google/go-tpm/issues/121
Lorenz Brunaa6b7342019-12-12 02:55:02 +010016
17import (
18 "crypto/aes"
19 "crypto/cipher"
20 "crypto/hmac"
21 "crypto/rsa"
22 "fmt"
23 "io"
24
25 "github.com/google/go-tpm/tpm2"
26 "github.com/google/go-tpm/tpmutil"
27)
28
29const (
30 labelIdentity = "IDENTITY"
31 labelStorage = "STORAGE"
32 labelIntegrity = "INTEGRITY"
33)
34
35func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) {
Lorenz Brund13c1c62022-03-30 19:58:58 +020036 aikHash, err := aik.Alg.Hash()
Lorenz Brunaa6b7342019-12-12 02:55:02 +010037 if err != nil {
38 return nil, nil, err
39 }
40
Serge Bazanski216fe7b2021-05-21 18:36:16 +020041 // The seed length should match the keysize used by the EKs symmetric
42 // cipher.
Lorenz Brunaa6b7342019-12-12 02:55:02 +010043 // For typical RSA EKs, this will be 128 bits (16 bytes).
44 // Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1.
45 seed := make([]byte, symBlockSize)
46 if _, err := io.ReadFull(rnd, seed); err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020047 return nil, nil, fmt.Errorf("generating seed: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010048 }
49
50 // Encrypt the seed value using the provided public key.
51 // See annex B, section 10.4 of the TPM specification revision 2 part 1.
52 label := append([]byte(labelIdentity), 0)
Lorenz Brund13c1c62022-03-30 19:58:58 +020053 encSecret, err := rsa.EncryptOAEP(aikHash.New(), rnd, pub, seed, label)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010054 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020055 return nil, nil, fmt.Errorf("generating encrypted seed: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010056 }
57
Serge Bazanski216fe7b2021-05-21 18:36:16 +020058 // Generate the encrypted credential by convolving the seed with the digest
59 // of the AIK, and using the result as the key to encrypt the secret.
Lorenz Brunaa6b7342019-12-12 02:55:02 +010060 // See section 24.4 of TPM 2.0 specification, part 1.
61 aikNameEncoded, err := aik.Encode()
62 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020063 return nil, nil, fmt.Errorf("encoding aikName: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010064 }
65 symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, len(seed)*8)
66 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020067 return nil, nil, fmt.Errorf("generating symmetric key: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010068 }
69 c, err := aes.NewCipher(symmetricKey)
70 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020071 return nil, nil, fmt.Errorf("symmetric cipher setup: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010072 }
73 cv, err := tpmutil.Pack(tpmutil.U16Bytes(secret))
74 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020075 return nil, nil, fmt.Errorf("generating cv (TPM2B_Digest): %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010076 }
77
78 // IV is all null bytes. encIdentity represents the encrypted credential.
79 encIdentity := make([]byte, len(cv))
80 cipher.NewCFBEncrypter(c, make([]byte, len(symmetricKey))).XORKeyStream(encIdentity, cv)
81
82 // Generate the integrity HMAC, which is used to protect the integrity of the
83 // encrypted structure.
84 // See section 24.5 of the TPM specification revision 2 part 1.
Lorenz Brund13c1c62022-03-30 19:58:58 +020085 macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, aikHash.Size()*8)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010086 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020087 return nil, nil, fmt.Errorf("generating HMAC key: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010088 }
89
Lorenz Brund13c1c62022-03-30 19:58:58 +020090 mac := hmac.New(aikHash.New, macKey)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010091 mac.Write(encIdentity)
92 mac.Write(aikNameEncoded)
93 integrityHMAC := mac.Sum(nil)
94
95 idObject := &tpm2.IDObject{
96 IntegrityHMAC: integrityHMAC,
97 EncIdentity: encIdentity,
98 }
99 id, err := tpmutil.Pack(idObject)
100 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200101 return nil, nil, fmt.Errorf("encoding IDObject: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100102 }
103
104 packedID, err := tpmutil.Pack(id)
105 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200106 return nil, nil, fmt.Errorf("packing id: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100107 }
108 packedEncSecret, err := tpmutil.Pack(encSecret)
109 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200110 return nil, nil, fmt.Errorf("packing encSecret: %w", err)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100111 }
112
113 return packedID, packedEncSecret, nil
114}