blob: 24766a79e007cacbc9cc5878baef0807c82c95a9 [file] [log] [blame]
Lorenz Brunaa6b7342019-12-12 02:55:02 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package tpm
18
Serge Bazanski216fe7b2021-05-21 18:36:16 +020019// This file is adapted from github.com/google/go-tpm/tpm2/credactivation which
20// outputs broken challenges for unknown reasons. They use u16 length-delimited
21// outputs for the challenge blobs which is incorrect. Rather than rewriting
22// the routine, we only applied minimal fixes to it and skip the ECC part of
23// the issue (because we would rather trust the proprietary RSA
24// implementation).
Leopold Schabel8fba0f82020-01-22 18:46:25 +010025//
Serge Bazanski216fe7b2021-05-21 18:36:16 +020026// TODO(lorenz): I'll eventually deal with this upstream, but for now just fix
27// it here (it's not that) much code after all.
28// https://github.com/google/go-tpm/issues/121
Lorenz Brunaa6b7342019-12-12 02:55:02 +010029
30import (
31 "crypto/aes"
32 "crypto/cipher"
33 "crypto/hmac"
34 "crypto/rsa"
35 "fmt"
36 "io"
37
38 "github.com/google/go-tpm/tpm2"
39 "github.com/google/go-tpm/tpmutil"
40)
41
42const (
43 labelIdentity = "IDENTITY"
44 labelStorage = "STORAGE"
45 labelIntegrity = "INTEGRITY"
46)
47
48func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) {
Lorenz Brund13c1c62022-03-30 19:58:58 +020049 aikHash, err := aik.Alg.Hash()
Lorenz Brunaa6b7342019-12-12 02:55:02 +010050 if err != nil {
51 return nil, nil, err
52 }
53
Serge Bazanski216fe7b2021-05-21 18:36:16 +020054 // The seed length should match the keysize used by the EKs symmetric
55 // cipher.
Lorenz Brunaa6b7342019-12-12 02:55:02 +010056 // For typical RSA EKs, this will be 128 bits (16 bytes).
57 // Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1.
58 seed := make([]byte, symBlockSize)
59 if _, err := io.ReadFull(rnd, seed); err != nil {
60 return nil, nil, fmt.Errorf("generating seed: %v", err)
61 }
62
63 // Encrypt the seed value using the provided public key.
64 // See annex B, section 10.4 of the TPM specification revision 2 part 1.
65 label := append([]byte(labelIdentity), 0)
Lorenz Brund13c1c62022-03-30 19:58:58 +020066 encSecret, err := rsa.EncryptOAEP(aikHash.New(), rnd, pub, seed, label)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010067 if err != nil {
68 return nil, nil, fmt.Errorf("generating encrypted seed: %v", err)
69 }
70
Serge Bazanski216fe7b2021-05-21 18:36:16 +020071 // Generate the encrypted credential by convolving the seed with the digest
72 // of the AIK, and using the result as the key to encrypt the secret.
Lorenz Brunaa6b7342019-12-12 02:55:02 +010073 // See section 24.4 of TPM 2.0 specification, part 1.
74 aikNameEncoded, err := aik.Encode()
75 if err != nil {
76 return nil, nil, fmt.Errorf("encoding aikName: %v", err)
77 }
78 symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, len(seed)*8)
79 if err != nil {
80 return nil, nil, fmt.Errorf("generating symmetric key: %v", err)
81 }
82 c, err := aes.NewCipher(symmetricKey)
83 if err != nil {
84 return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err)
85 }
86 cv, err := tpmutil.Pack(tpmutil.U16Bytes(secret))
87 if err != nil {
88 return nil, nil, fmt.Errorf("generating cv (TPM2B_Digest): %v", err)
89 }
90
91 // IV is all null bytes. encIdentity represents the encrypted credential.
92 encIdentity := make([]byte, len(cv))
93 cipher.NewCFBEncrypter(c, make([]byte, len(symmetricKey))).XORKeyStream(encIdentity, cv)
94
95 // Generate the integrity HMAC, which is used to protect the integrity of the
96 // encrypted structure.
97 // See section 24.5 of the TPM specification revision 2 part 1.
Lorenz Brund13c1c62022-03-30 19:58:58 +020098 macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, aikHash.Size()*8)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010099 if err != nil {
100 return nil, nil, fmt.Errorf("generating HMAC key: %v", err)
101 }
102
Lorenz Brund13c1c62022-03-30 19:58:58 +0200103 mac := hmac.New(aikHash.New, macKey)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100104 mac.Write(encIdentity)
105 mac.Write(aikNameEncoded)
106 integrityHMAC := mac.Sum(nil)
107
108 idObject := &tpm2.IDObject{
109 IntegrityHMAC: integrityHMAC,
110 EncIdentity: encIdentity,
111 }
112 id, err := tpmutil.Pack(idObject)
113 if err != nil {
114 return nil, nil, fmt.Errorf("encoding IDObject: %v", err)
115 }
116
117 packedID, err := tpmutil.Pack(id)
118 if err != nil {
119 return nil, nil, fmt.Errorf("packing id: %v", err)
120 }
121 packedEncSecret, err := tpmutil.Pack(encSecret)
122 if err != nil {
123 return nil, nil, fmt.Errorf("packing encSecret: %v", err)
124 }
125
126 return packedID, packedEncSecret, nil
127}