2021-07-24 13:16:34 +03:00
// Copyright 2021 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2021-07-24 13:16:34 +03:00
2021-12-10 11:14:24 +03:00
package asymkey
2021-07-24 13:16:34 +03:00
import (
2022-05-20 17:08:52 +03:00
"context"
2021-07-24 13:16:34 +03:00
"fmt"
"strings"
2021-09-19 14:49:59 +03:00
"code.gitea.io/gitea/models/db"
2021-07-24 13:16:34 +03:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
2021-11-17 15:34:35 +03:00
2021-07-24 13:16:34 +03:00
"golang.org/x/crypto/ssh"
2023-12-07 10:27:36 +03:00
"xorm.io/builder"
2021-07-24 13:16:34 +03:00
)
// ___________.__ .__ __
// \_ _____/|__| ____ ____ ________________________|__| _____/ |_
// | __) | |/ \ / ___\_/ __ \_ __ \____ \_ __ \ |/ \ __\
// | \ | | | \/ /_/ > ___/| | \/ |_> > | \/ | | \ |
// \___ / |__|___| /\___ / \___ >__| | __/|__| |__|___| /__|
// \/ \//_____/ \/ |__| \/
//
// This file contains functions for fingerprinting SSH keys
//
// The database is used in checkKeyFingerprint however most of these functions probably belong in a module
// checkKeyFingerprint only checks if key fingerprint has been used as public key,
// it is OK to use same key as deploy key for multiple repositories/users.
2022-05-20 17:08:52 +03:00
func checkKeyFingerprint ( ctx context . Context , fingerprint string ) error {
2023-12-07 10:27:36 +03:00
has , err := db . Exist [ PublicKey ] ( ctx , builder . Eq { "fingerprint" : fingerprint } )
2021-07-24 13:16:34 +03:00
if err != nil {
return err
} else if has {
return ErrKeyAlreadyExist { 0 , fingerprint , "" }
}
return nil
}
func calcFingerprintSSHKeygen ( publicKeyContent string ) ( string , error ) {
// Calculate fingerprint.
tmpPath , err := writeTmpKeyFile ( publicKeyContent )
if err != nil {
return "" , err
}
defer func ( ) {
if err := util . Remove ( tmpPath ) ; err != nil {
log . Warn ( "Unable to remove temporary key file: %s: Error: %v" , tmpPath , err )
}
} ( )
stdout , stderr , err := process . GetManager ( ) . Exec ( "AddPublicKey" , "ssh-keygen" , "-lf" , tmpPath )
if err != nil {
if strings . Contains ( stderr , "is not a public key file" ) {
return "" , ErrKeyUnableVerify { stderr }
}
2022-12-31 14:49:37 +03:00
return "" , util . NewInvalidArgumentErrorf ( "'ssh-keygen -lf %s' failed with error '%s': %s" , tmpPath , err , stderr )
2021-07-24 13:16:34 +03:00
} else if len ( stdout ) < 2 {
2022-12-31 14:49:37 +03:00
return "" , util . NewInvalidArgumentErrorf ( "not enough output for calculating fingerprint: %s" , stdout )
2021-07-24 13:16:34 +03:00
}
return strings . Split ( stdout , " " ) [ 1 ] , nil
}
func calcFingerprintNative ( publicKeyContent string ) ( string , error ) {
// Calculate fingerprint.
pk , _ , _ , _ , err := ssh . ParseAuthorizedKey ( [ ] byte ( publicKeyContent ) )
if err != nil {
return "" , err
}
return ssh . FingerprintSHA256 ( pk ) , nil
}
2022-06-04 22:18:50 +03:00
// CalcFingerprint calculate public key's fingerprint
func CalcFingerprint ( publicKeyContent string ) ( string , error ) {
2021-07-24 13:16:34 +03:00
// Call the method based on configuration
2024-04-07 14:17:06 +03:00
useNative := setting . SSH . KeygenPath == ""
calcFn := util . Iif ( useNative , calcFingerprintNative , calcFingerprintSSHKeygen )
fp , err := calcFn ( publicKeyContent )
2021-07-24 13:16:34 +03:00
if err != nil {
if IsErrKeyUnableVerify ( err ) {
return "" , err
}
2024-04-07 14:17:06 +03:00
return "" , fmt . Errorf ( "CalcFingerprint(%s): %w" , util . Iif ( useNative , "native" , "ssh-keygen" ) , err )
2021-07-24 13:16:34 +03:00
}
return fp , nil
}