@@ -79,37 +79,57 @@ func ProcessSignatureRequest(conf config.Config, sr shared.SignatureRequest) (re
7979 return
8080 }
8181
82+ // The key ID uniquely identifies the certificate by encoding the UUID of the request, a new UUID, and the username
83+ // Use both their uuid and our uuid to ensure it is unique
84+ keyID := sr .UUID + ":" + randomUUID .String () + ":" + sr .Username
85+
86+ log .Log (conf , fmt .Sprintf ("Processing SignatureRequest from user=%s on device='%s' keyID:%s, principals:%s, expiration:%s, pubkey:%s" ,
87+ sr .Username , sr .DeviceName , keyID , principals , conf .GetKeyExpiration (), sr .SSHPublicKey ))
88+ signature , err := SignKey (conf .GetCAKeyLocation (), keyID , principals , conf .GetKeyExpiration (), sr .SSHPublicKey )
89+
90+ return shared.SignatureResponse {SignedKey : signature , UUID : sr .UUID }, nil
91+ }
92+
93+ // Sign an SSH public key with the given data. Do so without any operations that rely on Keybase in order to ensure
94+ // that running `keybaseca sign` works even if Keybase is down.
95+ func SignKey (caKeyLocation , keyID , principals , expiration , publicKey string ) (signature string , err error ) {
96+ // Just a little bit of validation to give a nice error message
97+ if strings .Contains (publicKey , "PRIVATE KEY" ) {
98+ return "" , fmt .Errorf ("SignKey expects a public key (not a private key)" )
99+ }
100+
101+ // Write the public key to a temporary file
82102 tempFilename , err := getTempFilename ("keybase-ca-signed-key" )
83103 if err != nil {
84104 return
85105 }
86- err = ioutil .WriteFile (shared .KeyPathToPubKey (tempFilename ), []byte (sr . SSHPublicKey ), 0600 )
106+ err = ioutil .WriteFile (shared .KeyPathToPubKey (tempFilename ), []byte (publicKey ), 0600 )
87107 if err != nil {
88108 return
89109 }
90110
91- // The key ID uniquely identifies the certificate by encoding the UUID of the request, a new UUID, and the username
92- keyID := sr .UUID + ":" + randomUUID .String () + ":" + sr .Username
93-
94- log .Log (conf , fmt .Sprintf ("Processing SignatureRequest from user=%s on device='%s' keyID:%s, principals:%s, expiration:%s, pubkey:%s" ,
95- sr .Username , sr .DeviceName , keyID , principals , conf .GetKeyExpiration (), sr .SSHPublicKey ))
111+ // Note that we use ssh-keygen rather than Go's builtin SSH library since Go's SSH library does not support ed25519
112+ // SSH keys.
96113 cmd := exec .Command ("ssh-keygen" ,
97- "-s" , conf . GetCAKeyLocation () , // The CA key
98- "-I" , keyID , // The ID of the signed key. Use their uuid and our uuid to ensure it is unique
114+ "-s" , caKeyLocation , // The CA key
115+ "-I" , keyID , // A unique key ID
99116 "-n" , principals , // The allowed principals
100- "-V" , conf . GetKeyExpiration () , // The configured key expiration
117+ "-V" , expiration , // The expiration period for the key
101118 "-N" , "" , // No password on the key
102- shared .KeyPathToPubKey (tempFilename ), // The location of where to put the key
119+ shared .KeyPathToPubKey (tempFilename ), // The location of the public key
103120 )
104- err = cmd .Run ()
121+ bytes , err : = cmd .CombinedOutput ()
105122 if err != nil {
106- return
123+ return "" , fmt . Errorf ( "ssh-keygen error: %s (%v)" , strings . TrimSpace ( string ( bytes )), err )
107124 }
108125
109- data , err := ioutil .ReadFile (shared .KeyPathToCert (tempFilename ))
126+ // Read the certificate from the file
127+ signatureBytes , err := ioutil .ReadFile (shared .KeyPathToCert (tempFilename ))
110128 if err != nil {
111129 return
112130 }
131+
132+ // Delete the certificate and the pub key from the filesystem
113133 err = os .Remove (shared .KeyPathToPubKey (tempFilename ))
114134 if err != nil {
115135 return
@@ -118,7 +138,8 @@ func ProcessSignatureRequest(conf config.Config, sr shared.SignatureRequest) (re
118138 if err != nil {
119139 return
120140 }
121- return shared.SignatureResponse {SignedKey : string (data ), UUID : sr .UUID }, nil
141+
142+ return string (signatureBytes ), nil
122143}
123144
124145// Get the principals that should be placed in the signed certificate.
0 commit comments