github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/hsskey/hsskey.go (about) 1 // Copyright 2022 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package hsskey provides functionality for generating a key for unlocking 6 // drives based on the following procedure: 7 // 1. Via BMC, read a 32-byte secret seed known as the Host Secret Seed (HSS) 8 // using the OpenBMC IPMI blob transfer protocol 9 // 2. Compute a password as follows: 10 // We get the deterministically computed 32-byte HDKF-SHA256 using: 11 // - salt: "SKM PROD_V2 ACCESS" (default) 12 // - hss: 32-byte HSS 13 // - device identity: strings formed by concatenating the assembly serial 14 // number, the _ character, and the assembly part number. 15 package hsskey 16 17 import ( 18 "crypto/sha256" 19 "fmt" 20 "io" 21 "log" 22 "strings" 23 24 "github.com/mvdan/u-root-coreutils/pkg/ipmi" 25 "github.com/mvdan/u-root-coreutils/pkg/ipmi/blobs" 26 "golang.org/x/crypto/hkdf" 27 ) 28 29 type blobReader interface { 30 BlobOpen(id string, flags int16) (blobs.SessionID, error) 31 BlobRead(sid blobs.SessionID, offset, size uint32) ([]uint8, error) 32 BlobClose(sid blobs.SessionID) error 33 } 34 35 const ( 36 hostSecretSeedLen = 32 37 38 DefaultPasswordSalt = "SKM PROD_V2 ACCESS" 39 ) 40 41 // readHssBlob reads a host secret seed from the given blob id. 42 func readHssBlob(id string, h blobReader) (data []uint8, rerr error) { 43 sessionID, err := h.BlobOpen(id, blobs.BMC_BLOB_OPEN_FLAG_READ) 44 if err != nil { 45 return nil, fmt.Errorf("IPMI BlobOpen for %s failed: %v", id, err) 46 } 47 defer func() { 48 // If the function returned successfully but failed to close the blob, 49 // return an error. 50 if err := h.BlobClose(sessionID); err != nil && rerr == nil { 51 rerr = fmt.Errorf("IPMI BlobClose %s failed: %v", id, err) 52 } 53 }() 54 55 data, err = h.BlobRead(sessionID, 0, hostSecretSeedLen) 56 if err != nil { 57 return nil, fmt.Errorf("IPMI BlobRead %s failed: %v", id, err) 58 } 59 60 if len(data) != hostSecretSeedLen { 61 return nil, fmt.Errorf("HSS size incorrect: got %d for %s", len(data), id) 62 } 63 64 return data, nil 65 } 66 67 // GetAllHss reads all host secret seeds over IPMI. 68 func GetAllHss(verbose bool, verboseDangerous bool) ([][]uint8, error) { 69 i, err := ipmi.Open(0) 70 if err != nil { 71 return nil, err 72 } 73 h := blobs.NewBlobHandler(i) 74 75 blobCount, err := h.BlobGetCount() 76 if err != nil { 77 return nil, fmt.Errorf("failed to get blob count: %v", err) 78 } 79 80 hssList := [][]uint8{} 81 seen := make(map[string]bool) 82 skmSubstr := "/skm/hss/" 83 84 // Read from all */skm/hss/* blobs. 85 for j := 0; j < blobCount; j++ { 86 id, err := h.BlobEnumerate(j) 87 if err != nil { 88 return nil, fmt.Errorf("failed to enumerate blob %d: %v", j, err) 89 } 90 91 if !strings.Contains(id, skmSubstr) { 92 continue 93 } 94 95 hss, err := readHssBlob(id, h) 96 if err != nil { 97 log.Printf("failed to read HSS of id %s: %v", id, err) 98 continue 99 } 100 101 if verbose { 102 msg := fmt.Sprintf("HSS Entry: Id=%s", id) 103 if verboseDangerous { 104 msg = msg + fmt.Sprintf(",Seed=%x", hss) 105 } 106 log.Print(msg) 107 } 108 109 hssStr := fmt.Sprint(hss) 110 if !seen[hssStr] { 111 seen[hssStr] = true 112 hssList = append(hssList, hss) 113 } 114 } 115 116 return hssList, nil 117 } 118 119 // GenPassword computes the password deterministically as the 32-byte HDKF-SHA256 of the 120 // HSS plus the device identity. 121 func GenPassword(hss []byte, salt string, identifiers ...string) ([]byte, error) { 122 hash := sha256.New 123 devID := strings.Join(identifiers, "_") 124 125 r := hkdf.New(hash, hss, ([]byte)(salt), ([]byte)(devID)) 126 key := make([]byte, 32) 127 128 if _, err := io.ReadFull(r, key); err != nil { 129 return nil, err 130 } 131 return key, nil 132 }