|
1 | 1 | package main |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "crypto/rand" |
4 | 5 | "crypto/rsa" |
5 | 6 | "crypto/x509" |
6 | 7 | "encoding/pem" |
7 | 8 | "fmt" |
| 9 | + "io" |
| 10 | + "math/big" |
8 | 11 | "os" |
9 | 12 | "strings" |
10 | 13 |
|
@@ -121,51 +124,73 @@ var ( |
121 | 124 | } |
122 | 125 |
|
123 | 126 | sgxSetSigCmd = &cobra.Command{ |
124 | | - Use: "sgx-set-sig [--component ID] <bundle.orc> <signature.sig> <public_key.pub>", |
| 127 | + Use: "sgx-set-sig [--component ID] <bundle.orc> [<signature.sig> <public_key.pub>]", |
125 | 128 | Short: "add or overwrite an SGXS signature to an existing runtime bundle", |
126 | | - Args: cobra.ExactArgs(3), |
| 129 | + Args: cobra.RangeArgs(1, 3), |
127 | 130 | Run: func(cmd *cobra.Command, args []string) { |
128 | | - bundlePath, sigPath, publicKey := args[0], args[1], args[2] |
| 131 | + var sigPath, publicKey string |
| 132 | + bundlePath := args[0] |
| 133 | + switch len(args) { |
| 134 | + case 1: |
| 135 | + case 3: |
| 136 | + sigPath, publicKey = args[1], args[2] |
| 137 | + default: |
| 138 | + cobra.CheckErr("unsupported number of arguments") |
| 139 | + } |
129 | 140 | compId := getComponentID() |
130 | 141 |
|
131 | 142 | rawCompId, _ := compId.MarshalText() |
132 | 143 | sgxSigName := fmt.Sprintf(sgxSigNameFmt, string(rawCompId)) |
133 | 144 |
|
134 | | - // Load public key. |
135 | | - rawPub, err := os.ReadFile(publicKey) |
136 | | - if err != nil { |
137 | | - cobra.CheckErr(fmt.Errorf("failed to read public key: %w", err)) |
138 | | - } |
139 | | - pubPem, _ := pem.Decode(rawPub) |
140 | | - if pubPem == nil { |
141 | | - cobra.CheckErr(fmt.Errorf("failed to decode public key pem file")) |
142 | | - } |
143 | | - pub, err := x509.ParsePKIXPublicKey(pubPem.Bytes) |
144 | | - if err != nil { |
145 | | - cobra.CheckErr(fmt.Errorf("failed to parse public key: %w", err)) |
146 | | - } |
147 | | - pubKey, ok := pub.(*rsa.PublicKey) |
148 | | - if !ok { |
149 | | - cobra.CheckErr(fmt.Errorf("invalid public key type: %T", pub)) |
150 | | - } |
151 | | - |
152 | 145 | // Load bundle. |
153 | 146 | bnd, err := bundle.Open(bundlePath) |
154 | 147 | if err != nil { |
155 | 148 | cobra.CheckErr(fmt.Errorf("failed to open bundle: %w", err)) |
156 | 149 | } |
157 | 150 |
|
158 | | - // Load signature file. |
159 | | - rawSig, err := os.ReadFile(sigPath) |
160 | | - if err != nil { |
161 | | - cobra.CheckErr(fmt.Errorf("failed to load signature file: %w", err)) |
162 | | - } |
163 | | - |
164 | 151 | // Construct sigstruct from provided arguments. |
| 152 | + var signed []byte |
165 | 153 | sigstruct := constructSigstruct(bnd, compId) |
166 | | - signed, err := sigstruct.WithSignature(rawSig, pubKey) |
167 | | - if err != nil { |
168 | | - cobra.CheckErr(fmt.Errorf("failed to append signature: %w", err)) |
| 154 | + switch sigPath { |
| 155 | + case "": |
| 156 | + // Generate a new random key and sign the sigstruct. |
| 157 | + sigKey, err := sgxGenerateKey(rand.Reader) |
| 158 | + if err != nil { |
| 159 | + cobra.CheckErr(fmt.Errorf("failed to generate signer key: %w", err)) |
| 160 | + } |
| 161 | + signed, err = sigstruct.Sign(sigKey) |
| 162 | + if err != nil { |
| 163 | + cobra.CheckErr(fmt.Errorf("failed to sign SIGSTRUCT: %w", err)) |
| 164 | + } |
| 165 | + default: |
| 166 | + // Load public key. |
| 167 | + rawPub, err := os.ReadFile(publicKey) |
| 168 | + if err != nil { |
| 169 | + cobra.CheckErr(fmt.Errorf("failed to read public key: %w", err)) |
| 170 | + } |
| 171 | + pubPem, _ := pem.Decode(rawPub) |
| 172 | + if pubPem == nil { |
| 173 | + cobra.CheckErr(fmt.Errorf("failed to decode public key pem file")) |
| 174 | + } |
| 175 | + pub, err := x509.ParsePKIXPublicKey(pubPem.Bytes) |
| 176 | + if err != nil { |
| 177 | + cobra.CheckErr(fmt.Errorf("failed to parse public key: %w", err)) |
| 178 | + } |
| 179 | + pubKey, ok := pub.(*rsa.PublicKey) |
| 180 | + if !ok { |
| 181 | + cobra.CheckErr(fmt.Errorf("invalid public key type: %T", pub)) |
| 182 | + } |
| 183 | + |
| 184 | + // Load signature file. |
| 185 | + rawSig, err := os.ReadFile(sigPath) |
| 186 | + if err != nil { |
| 187 | + cobra.CheckErr(fmt.Errorf("failed to load signature file: %w", err)) |
| 188 | + } |
| 189 | + |
| 190 | + signed, err = sigstruct.WithSignature(rawSig, pubKey) |
| 191 | + if err != nil { |
| 192 | + cobra.CheckErr(fmt.Errorf("failed to append signature: %w", err)) |
| 193 | + } |
169 | 194 | } |
170 | 195 | err = bnd.Add(sgxSigName, bundle.NewBytesData(signed)) |
171 | 196 | cobra.CheckErr(err) |
@@ -531,6 +556,84 @@ func showTdxComponent(indent string, bnd *bundle.Bundle, comp *bundle.Component) |
531 | 556 | fmt.Printf("%s Memory: %d MiB\n", indent, comp.TDX.Resources.Memory) |
532 | 557 | } |
533 | 558 |
|
| 559 | +// sgxGenerateKey generates a 3072-bit RSA key with public exponent 3 as required for SGX. |
| 560 | +// |
| 561 | +// The code below is adopted from the Go standard library as it is otherwise not possible to |
| 562 | +// customize the exponent. |
| 563 | +func sgxGenerateKey(random io.Reader) (*rsa.PrivateKey, error) { |
| 564 | + priv := new(rsa.PrivateKey) |
| 565 | + priv.E = 3 |
| 566 | + bits := 3072 |
| 567 | + nprimes := 2 |
| 568 | + |
| 569 | + bigOne := big.NewInt(1) |
| 570 | + primes := make([]*big.Int, nprimes) |
| 571 | + |
| 572 | +NextSetOfPrimes: |
| 573 | + for { |
| 574 | + todo := bits |
| 575 | + // crypto/rand should set the top two bits in each prime. |
| 576 | + // Thus each prime has the form |
| 577 | + // p_i = 2^bitlen(p_i) × 0.11... (in base 2). |
| 578 | + // And the product is: |
| 579 | + // P = 2^todo × α |
| 580 | + // where α is the product of nprimes numbers of the form 0.11... |
| 581 | + // |
| 582 | + // If α < 1/2 (which can happen for nprimes > 2), we need to |
| 583 | + // shift todo to compensate for lost bits: the mean value of 0.11... |
| 584 | + // is 7/8, so todo + shift - nprimes * log2(7/8) ~= bits - 1/2 |
| 585 | + // will give good results. |
| 586 | + if nprimes >= 7 { |
| 587 | + todo += (nprimes - 2) / 5 |
| 588 | + } |
| 589 | + for i := 0; i < nprimes; i++ { |
| 590 | + var err error |
| 591 | + primes[i], err = rand.Prime(random, todo/(nprimes-i)) |
| 592 | + if err != nil { |
| 593 | + return nil, err |
| 594 | + } |
| 595 | + todo -= primes[i].BitLen() |
| 596 | + } |
| 597 | + |
| 598 | + // Make sure that primes is pairwise unequal. |
| 599 | + for i, prime := range primes { |
| 600 | + for j := 0; j < i; j++ { |
| 601 | + if prime.Cmp(primes[j]) == 0 { |
| 602 | + continue NextSetOfPrimes |
| 603 | + } |
| 604 | + } |
| 605 | + } |
| 606 | + |
| 607 | + n := new(big.Int).Set(bigOne) |
| 608 | + totient := new(big.Int).Set(bigOne) |
| 609 | + pminus1 := new(big.Int) |
| 610 | + for _, prime := range primes { |
| 611 | + n.Mul(n, prime) |
| 612 | + pminus1.Sub(prime, bigOne) |
| 613 | + totient.Mul(totient, pminus1) |
| 614 | + } |
| 615 | + if n.BitLen() != bits { |
| 616 | + // This should never happen for nprimes == 2 because |
| 617 | + // crypto/rand should set the top two bits in each prime. |
| 618 | + // For nprimes > 2 we hope it does not happen often. |
| 619 | + continue NextSetOfPrimes |
| 620 | + } |
| 621 | + |
| 622 | + priv.D = new(big.Int) |
| 623 | + e := big.NewInt(int64(priv.E)) |
| 624 | + ok := priv.D.ModInverse(e, totient) |
| 625 | + |
| 626 | + if ok != nil { |
| 627 | + priv.Primes = primes |
| 628 | + priv.N = n |
| 629 | + break |
| 630 | + } |
| 631 | + } |
| 632 | + |
| 633 | + priv.Precompute() |
| 634 | + return priv, nil |
| 635 | +} |
| 636 | + |
534 | 637 | func main() { |
535 | 638 | _ = rootCmd.Execute() |
536 | 639 | } |
|
0 commit comments