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