2017-09-26 20:16:18 -04:00
package trust
import (
2017-10-10 13:16:01 -04:00
"bytes"
2017-10-25 13:45:10 -04:00
"encoding/pem"
2017-09-26 20:16:18 -04:00
"fmt"
2022-02-25 08:33:57 -05:00
"io"
2017-09-26 20:16:18 -04:00
"os"
2019-06-24 18:12:01 -04:00
"runtime"
2017-09-26 20:16:18 -04:00
"github.com/docker/cli/cli"
"github.com/docker/cli/cli/command"
"github.com/docker/cli/cli/trust"
2017-10-25 13:13:24 -04:00
"github.com/pkg/errors"
2017-09-26 20:16:18 -04:00
"github.com/spf13/cobra"
2017-10-30 12:21:41 -04:00
"github.com/theupdateframework/notary"
"github.com/theupdateframework/notary/storage"
"github.com/theupdateframework/notary/trustmanager"
tufutils "github.com/theupdateframework/notary/tuf/utils"
2017-09-26 20:16:18 -04:00
)
const (
2022-09-30 13:13:22 -04:00
nonOwnerReadWriteMask = 0 o077
2017-09-26 20:16:18 -04:00
)
type keyLoadOptions struct {
keyName string
}
func newKeyLoadCommand ( dockerCli command . Streams ) * cobra . Command {
var options keyLoadOptions
cmd := & cobra . Command {
2017-10-25 13:45:10 -04:00
Use : "load [OPTIONS] KEYFILE" ,
2017-09-26 20:16:18 -04:00
Short : "Load a private key file for signing" ,
Args : cli . ExactArgs ( 1 ) ,
RunE : func ( cmd * cobra . Command , args [ ] string ) error {
return loadPrivKey ( dockerCli , args [ 0 ] , options )
} ,
}
flags := cmd . Flags ( )
2017-10-25 13:45:10 -04:00
flags . StringVar ( & options . keyName , "name" , "signer" , "Name for the loaded key" )
2017-09-26 20:16:18 -04:00
return cmd
}
func loadPrivKey ( streams command . Streams , keyPath string , options keyLoadOptions ) error {
2017-10-25 13:45:10 -04:00
// validate the key name if provided
if options . keyName != "" && ! validKeyName ( options . keyName ) {
return fmt . Errorf ( "key name \"%s\" must start with lowercase alphanumeric characters and can include \"-\" or \"_\" after the first character" , options . keyName )
}
2017-09-26 20:16:18 -04:00
trustDir := trust . GetTrustDirectory ( )
2017-10-10 13:16:01 -04:00
keyFileStore , err := storage . NewPrivateKeyFileStorage ( trustDir , notary . KeyExtension )
2017-09-26 20:16:18 -04:00
if err != nil {
return err
}
2017-10-23 05:23:39 -04:00
privKeyImporters := [ ] trustmanager . Importer { keyFileStore }
2017-09-26 20:16:18 -04:00
2017-10-25 13:45:10 -04:00
fmt . Fprintf ( streams . Out ( ) , "Loading key from \"%s\"...\n" , keyPath )
2017-09-26 20:16:18 -04:00
// Always use a fresh passphrase retriever for each import
passRet := trust . GetPassphraseRetriever ( streams . In ( ) , streams . Out ( ) )
2017-10-10 13:16:01 -04:00
keyBytes , err := getPrivKeyBytesFromPath ( keyPath )
if err != nil {
2017-10-25 13:45:10 -04:00
return errors . Wrapf ( err , "refusing to load key from %s" , keyPath )
2017-10-10 13:16:01 -04:00
}
if err := loadPrivKeyBytesToStore ( keyBytes , privKeyImporters , keyPath , options . keyName , passRet ) ; err != nil {
2017-10-25 13:13:24 -04:00
return errors . Wrapf ( err , "error importing key from %s" , keyPath )
2017-09-26 20:16:18 -04:00
}
fmt . Fprintf ( streams . Out ( ) , "Successfully imported key from %s\n" , keyPath )
return nil
}
2017-10-10 13:16:01 -04:00
func getPrivKeyBytesFromPath ( keyPath string ) ( [ ] byte , error ) {
2019-06-24 18:12:01 -04:00
if runtime . GOOS != "windows" {
fileInfo , err := os . Stat ( keyPath )
if err != nil {
return nil , err
}
if fileInfo . Mode ( ) & nonOwnerReadWriteMask != 0 {
return nil , fmt . Errorf ( "private key file %s must not be readable or writable by others" , keyPath )
}
2017-09-26 20:16:18 -04:00
}
from , err := os . OpenFile ( keyPath , os . O_RDONLY , notary . PrivExecPerms )
if err != nil {
2017-10-10 13:16:01 -04:00
return nil , err
2017-09-26 20:16:18 -04:00
}
defer from . Close ( )
2022-02-25 08:33:57 -05:00
return io . ReadAll ( from )
2017-10-10 13:16:01 -04:00
}
2017-10-23 05:23:39 -04:00
func loadPrivKeyBytesToStore ( privKeyBytes [ ] byte , privKeyImporters [ ] trustmanager . Importer , keyPath , keyName string , passRet notary . PassRetriever ) error {
2017-10-25 13:45:10 -04:00
var err error
if _ , _ , err = tufutils . ExtractPrivateKeyAttributes ( privKeyBytes ) ; err != nil {
2017-10-09 13:44:52 -04:00
return fmt . Errorf ( "provided file %s is not a supported private key - to add a signer's public key use docker trust signer add" , keyPath )
2017-09-26 20:16:18 -04:00
}
2017-10-25 13:45:10 -04:00
if privKeyBytes , err = decodePrivKeyIfNecessary ( privKeyBytes , passRet ) ; err != nil {
return errors . Wrapf ( err , "cannot load key from provided file %s" , keyPath )
}
2017-10-10 13:16:01 -04:00
// Make a reader, rewind the file pointer
2017-10-23 05:23:39 -04:00
return trustmanager . ImportKeys ( bytes . NewReader ( privKeyBytes ) , privKeyImporters , keyName , "" , passRet )
2017-09-26 20:16:18 -04:00
}
2017-10-25 13:45:10 -04:00
func decodePrivKeyIfNecessary ( privPemBytes [ ] byte , passRet notary . PassRetriever ) ( [ ] byte , error ) {
pemBlock , _ := pem . Decode ( privPemBytes )
_ , containsDEKInfo := pemBlock . Headers [ "DEK-Info" ]
if containsDEKInfo || pemBlock . Type == "ENCRYPTED PRIVATE KEY" {
// if we do not have enough information to properly import, try to decrypt the key
if _ , ok := pemBlock . Headers [ "path" ] ; ! ok {
privKey , _ , err := trustmanager . GetPasswdDecryptBytes ( passRet , privPemBytes , "" , "encrypted" )
if err != nil {
return [ ] byte { } , fmt . Errorf ( "could not decrypt key" )
}
privPemBytes = privKey . Private ( )
}
}
return privPemBytes , nil
}