blob: 039f8d5165fb3abe0d99152bcd2bdb8b9138ef11 [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
19// This file is adapted from github.com/google/go-tpm/tpm2/credactivation which outputs broken
20// challenges for unknown reasons. They use u16 length-delimited outputs for the challenge blobs
Leopold Schabel8fba0f82020-01-22 18:46:25 +010021// which is incorrect. Rather than rewriting the routine, we only applied minimal fixes to it
22// and skip the ECC part of the issue (because we would rather trust the proprietary RSA implementation).
23//
Lorenz Brunaa6b7342019-12-12 02:55:02 +010024// TODO(lorenz): I'll eventually deal with this upstream, but for now just fix it here (it's not that)
Leopold Schabel8fba0f82020-01-22 18:46:25 +010025// much code after all (https://github.com/google/go-tpm/issues/121)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010026
27import (
28 "crypto/aes"
29 "crypto/cipher"
30 "crypto/hmac"
31 "crypto/rsa"
32 "fmt"
33 "io"
34
35 "github.com/google/go-tpm/tpm2"
36 "github.com/google/go-tpm/tpmutil"
37)
38
39const (
40 labelIdentity = "IDENTITY"
41 labelStorage = "STORAGE"
42 labelIntegrity = "INTEGRITY"
43)
44
45func generateRSA(aik *tpm2.HashValue, pub *rsa.PublicKey, symBlockSize int, secret []byte, rnd io.Reader) ([]byte, []byte, error) {
46 newAIKHash, err := aik.Alg.HashConstructor()
47 if err != nil {
48 return nil, nil, err
49 }
50
51 // The seed length should match the keysize used by the EKs symmetric cipher.
52 // For typical RSA EKs, this will be 128 bits (16 bytes).
53 // Spec: TCG 2.0 EK Credential Profile revision 14, section 2.1.5.1.
54 seed := make([]byte, symBlockSize)
55 if _, err := io.ReadFull(rnd, seed); err != nil {
56 return nil, nil, fmt.Errorf("generating seed: %v", err)
57 }
58
59 // Encrypt the seed value using the provided public key.
60 // See annex B, section 10.4 of the TPM specification revision 2 part 1.
61 label := append([]byte(labelIdentity), 0)
62 encSecret, err := rsa.EncryptOAEP(newAIKHash(), rnd, pub, seed, label)
63 if err != nil {
64 return nil, nil, fmt.Errorf("generating encrypted seed: %v", err)
65 }
66
67 // Generate the encrypted credential by convolving the seed with the digest of
68 // the AIK, and using the result as the key to encrypt the secret.
69 // See section 24.4 of TPM 2.0 specification, part 1.
70 aikNameEncoded, err := aik.Encode()
71 if err != nil {
72 return nil, nil, fmt.Errorf("encoding aikName: %v", err)
73 }
74 symmetricKey, err := tpm2.KDFa(aik.Alg, seed, labelStorage, aikNameEncoded, nil, len(seed)*8)
75 if err != nil {
76 return nil, nil, fmt.Errorf("generating symmetric key: %v", err)
77 }
78 c, err := aes.NewCipher(symmetricKey)
79 if err != nil {
80 return nil, nil, fmt.Errorf("symmetric cipher setup: %v", err)
81 }
82 cv, err := tpmutil.Pack(tpmutil.U16Bytes(secret))
83 if err != nil {
84 return nil, nil, fmt.Errorf("generating cv (TPM2B_Digest): %v", err)
85 }
86
87 // IV is all null bytes. encIdentity represents the encrypted credential.
88 encIdentity := make([]byte, len(cv))
89 cipher.NewCFBEncrypter(c, make([]byte, len(symmetricKey))).XORKeyStream(encIdentity, cv)
90
91 // Generate the integrity HMAC, which is used to protect the integrity of the
92 // encrypted structure.
93 // See section 24.5 of the TPM specification revision 2 part 1.
94 macKey, err := tpm2.KDFa(aik.Alg, seed, labelIntegrity, nil, nil, newAIKHash().Size()*8)
95 if err != nil {
96 return nil, nil, fmt.Errorf("generating HMAC key: %v", err)
97 }
98
99 mac := hmac.New(newAIKHash, macKey)
100 mac.Write(encIdentity)
101 mac.Write(aikNameEncoded)
102 integrityHMAC := mac.Sum(nil)
103
104 idObject := &tpm2.IDObject{
105 IntegrityHMAC: integrityHMAC,
106 EncIdentity: encIdentity,
107 }
108 id, err := tpmutil.Pack(idObject)
109 if err != nil {
110 return nil, nil, fmt.Errorf("encoding IDObject: %v", err)
111 }
112
113 packedID, err := tpmutil.Pack(id)
114 if err != nil {
115 return nil, nil, fmt.Errorf("packing id: %v", err)
116 }
117 packedEncSecret, err := tpmutil.Pack(encSecret)
118 if err != nil {
119 return nil, nil, fmt.Errorf("packing encSecret: %v", err)
120 }
121
122 return packedID, packedEncSecret, nil
123}