mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in NOTICE, alongside LICENSE. The header lint accepts any copyright line above the SPDX line, so it no longer demands an upstream one. Co-authored-by: Hanzo Dev <[email protected]>
162 lines
4.3 KiB
Go
162 lines
4.3 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package asymkey
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/hanzoai/git/models/db"
|
|
"github.com/hanzoai/git/modules/log"
|
|
|
|
"github.com/ProtonMail/go-crypto/openpgp"
|
|
)
|
|
|
|
// __________________ ________ ____ __.
|
|
// / _____/\______ \/ _____/ | |/ _|____ ___.__.
|
|
// / \ ___ | ___/ \ ___ | <_/ __ < | |
|
|
// \ \_\ \| | \ \_\ \ | | \ ___/\___ |
|
|
// \______ /|____| \______ / |____|__ \___ > ____|
|
|
// \/ \/ \/ \/\/
|
|
// _____ .___ .___
|
|
// / _ \ __| _/__| _/
|
|
// / /_\ \ / __ |/ __ |
|
|
// / | \/ /_/ / /_/ |
|
|
// \____|__ /\____ \____ |
|
|
// \/ \/ \/
|
|
|
|
// This file contains functions relating to adding GPG Keys
|
|
|
|
// addGPGKey add key, import and subkeys to database
|
|
func addGPGKey(ctx context.Context, key *GPGKey, content string) (err error) {
|
|
// Add GPGKeyImport
|
|
if err = db.Insert(ctx, &GPGKeyImport{
|
|
KeyID: key.KeyID,
|
|
Content: content,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
// Save GPG primary key.
|
|
if err = db.Insert(ctx, key); err != nil {
|
|
return err
|
|
}
|
|
// Save GPG subs key.
|
|
for _, subkey := range key.SubsKey {
|
|
if err := addGPGSubKey(ctx, subkey); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// addGPGSubKey add subkeys to database
|
|
func addGPGSubKey(ctx context.Context, key *GPGKey) (err error) {
|
|
// Save GPG primary key.
|
|
if err = db.Insert(ctx, key); err != nil {
|
|
return err
|
|
}
|
|
// Save GPG subs key.
|
|
for _, subkey := range key.SubsKey {
|
|
if err := addGPGSubKey(ctx, subkey); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AddGPGKey adds new public key to database.
|
|
func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature string) ([]*GPGKey, error) {
|
|
ekeys, err := CheckArmoredGPGKeyString(content)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return db.WithTx2(ctx, func(ctx context.Context) ([]*GPGKey, error) {
|
|
keys := make([]*GPGKey, 0, len(ekeys))
|
|
|
|
verified := false
|
|
// Handle provided signature
|
|
if signature != "" {
|
|
signer, err := openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token), strings.NewReader(signature), nil)
|
|
if err != nil {
|
|
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\n"), strings.NewReader(signature), nil)
|
|
}
|
|
if err != nil {
|
|
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil)
|
|
}
|
|
if err != nil {
|
|
log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err)
|
|
return nil, ErrGPGInvalidTokenSignature{
|
|
ID: ekeys[0].PrimaryKey.KeyIdString(),
|
|
Wrapped: err,
|
|
}
|
|
}
|
|
ekeys = []*openpgp.Entity{signer}
|
|
verified = true
|
|
}
|
|
|
|
if len(ekeys) > 1 {
|
|
id2key := map[string]*openpgp.Entity{}
|
|
newEKeys := make([]*openpgp.Entity, 0, len(ekeys))
|
|
for _, ekey := range ekeys {
|
|
id := ekey.PrimaryKey.KeyIdString()
|
|
if original, has := id2key[id]; has {
|
|
// Coalesce this with the other one
|
|
for _, subkey := range ekey.Subkeys {
|
|
if subkey.PublicKey == nil {
|
|
continue
|
|
}
|
|
found := false
|
|
|
|
for _, originalSubkey := range original.Subkeys {
|
|
if originalSubkey.PublicKey == nil {
|
|
continue
|
|
}
|
|
if originalSubkey.PublicKey.KeyId == subkey.PublicKey.KeyId {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
original.Subkeys = append(original.Subkeys, subkey)
|
|
}
|
|
}
|
|
for name, identity := range ekey.Identities {
|
|
if _, has := original.Identities[name]; has {
|
|
continue
|
|
}
|
|
original.Identities[name] = identity
|
|
}
|
|
continue
|
|
}
|
|
id2key[id] = ekey
|
|
newEKeys = append(newEKeys, ekey)
|
|
}
|
|
ekeys = newEKeys
|
|
}
|
|
|
|
for _, ekey := range ekeys {
|
|
// Key ID cannot be duplicated.
|
|
has, err := db.GetEngine(ctx).Where("key_id=?", ekey.PrimaryKey.KeyIdString()).
|
|
Get(new(GPGKey))
|
|
if err != nil {
|
|
return nil, err
|
|
} else if has {
|
|
return nil, ErrGPGKeyIDAlreadyUsed{ekey.PrimaryKey.KeyIdString()}
|
|
}
|
|
|
|
key, err := parseGPGKey(ctx, ownerID, ekey, verified)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = addGPGKey(ctx, key, content); err != nil {
|
|
return nil, err
|
|
}
|
|
keys = append(keys, key)
|
|
}
|
|
return keys, nil
|
|
})
|
|
}
|