github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/pgp_gen.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"crypto"
     8  	"fmt"
     9  	"strings"
    10  
    11  	"github.com/keybase/go-crypto/openpgp"
    12  	"github.com/keybase/go-crypto/openpgp/errors"
    13  	"github.com/keybase/go-crypto/openpgp/packet"
    14  	"github.com/keybase/go-crypto/openpgp/s2k"
    15  	"github.com/keybase/go-crypto/rsa"
    16  )
    17  
    18  type PGPGenArg struct {
    19  	PrimaryBits     int
    20  	SubkeyBits      int
    21  	Ids             Identities
    22  	Config          *packet.Config
    23  	PGPUids         []string
    24  	PrimaryLifetime int
    25  	SubkeyLifetime  int
    26  }
    27  
    28  func ui32p(i int) *uint32 {
    29  	if i >= 0 {
    30  		tmp := uint32(i)
    31  		return &tmp
    32  	}
    33  	return nil
    34  }
    35  
    36  // NewEntity returns an Entity that contains a fresh RSA/RSA keypair with a
    37  // single identity composed of the given full name, comment and email, any of
    38  // which may be empty but must not contain any of "()<>\x00".
    39  // If config is nil, sensible defaults will be used.
    40  //
    41  // Modification of: https://code.google.com/p/go/source/browse/openpgp/keys.go?repo=crypto&r=8fec09c61d5d66f460d227fd1df3473d7e015bc6#456
    42  //
    43  //	From golang.com/x/crypto/openpgp/keys.go
    44  func GeneratePGPKeyBundle(g *GlobalContext, arg PGPGenArg, logUI LogUI) (*PGPKeyBundle, error) {
    45  	currentTime := arg.Config.Now()
    46  
    47  	if len(arg.Ids) == 0 {
    48  		return nil, errors.InvalidArgumentError("No Ids in PGPArg")
    49  	}
    50  	uids, err := arg.PGPUserIDs()
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	for i, id := range arg.Ids {
    55  		extra := ""
    56  		if i == 0 {
    57  			extra = "[primary]"
    58  		}
    59  		if logUI != nil {
    60  			logUI.Info("PGP User ID: %s %s", id, extra)
    61  		}
    62  	}
    63  
    64  	if logUI != nil {
    65  		logUI.Info("Generating primary key (%d bits)", arg.PrimaryBits)
    66  	}
    67  	masterPriv, err := rsa.GenerateKey(arg.Config.Random(), arg.PrimaryBits)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	if logUI != nil {
    73  		logUI.Info("Generating encryption subkey (%d bits)", arg.SubkeyBits)
    74  	}
    75  	encryptingPriv, err := rsa.GenerateKey(arg.Config.Random(), arg.SubkeyBits)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	e := &openpgp.Entity{
    81  		PrimaryKey: packet.NewRSAPublicKey(currentTime, &masterPriv.PublicKey),
    82  		PrivateKey: packet.NewRSAPrivateKey(currentTime, masterPriv),
    83  		Identities: make(map[string]*openpgp.Identity),
    84  	}
    85  
    86  	for i, uid := range uids {
    87  		isPrimaryID := true
    88  		if i > 0 {
    89  			isPrimaryID = false
    90  		}
    91  		id := &openpgp.Identity{
    92  			Name:   uid.Name,
    93  			UserId: uid,
    94  			SelfSignature: &packet.Signature{
    95  				CreationTime:         currentTime,
    96  				SigType:              packet.SigTypePositiveCert,
    97  				PubKeyAlgo:           packet.PubKeyAlgoRSA,
    98  				Hash:                 arg.Config.Hash(),
    99  				IsPrimaryId:          &isPrimaryID,
   100  				FlagsValid:           true,
   101  				FlagSign:             true,
   102  				FlagCertify:          true,
   103  				IssuerKeyId:          &e.PrimaryKey.KeyId,
   104  				PreferredSymmetric:   arg.PreferredSymmetric(),
   105  				PreferredHash:        arg.PreferredHash(),
   106  				PreferredCompression: arg.PreferredCompression(),
   107  			},
   108  		}
   109  		id.SelfSignature.KeyLifetimeSecs = ui32p(arg.PrimaryLifetime)
   110  		e.Identities[uid.Id] = id
   111  	}
   112  
   113  	e.Subkeys = make([]openpgp.Subkey, 1)
   114  	e.Subkeys[0] = openpgp.Subkey{
   115  		PublicKey:  packet.NewRSAPublicKey(currentTime, &encryptingPriv.PublicKey),
   116  		PrivateKey: packet.NewRSAPrivateKey(currentTime, encryptingPriv),
   117  		Sig: &packet.Signature{
   118  			CreationTime:              currentTime,
   119  			SigType:                   packet.SigTypeSubkeyBinding,
   120  			PubKeyAlgo:                packet.PubKeyAlgoRSA,
   121  			Hash:                      arg.Config.Hash(),
   122  			FlagsValid:                true,
   123  			FlagEncryptStorage:        true,
   124  			FlagEncryptCommunications: true,
   125  			IssuerKeyId:               &e.PrimaryKey.KeyId,
   126  			PreferredSymmetric:        arg.PreferredSymmetric(),
   127  			PreferredHash:             arg.PreferredHash(),
   128  			PreferredCompression:      arg.PreferredCompression(),
   129  		},
   130  	}
   131  	e.Subkeys[0].PublicKey.IsSubkey = true
   132  	e.Subkeys[0].PrivateKey.IsSubkey = true
   133  	e.Subkeys[0].Sig.KeyLifetimeSecs = ui32p(arg.SubkeyLifetime)
   134  
   135  	return NewGeneratedPGPKeyBundle(e), nil
   136  }
   137  
   138  // CreateIDs creates identities for KeyGenArg.Ids if none exist.
   139  // It uses PGPUids to determine the set of Ids.  It does not set the
   140  // default keybase.io uid.  AddDefaultUid() does that.
   141  func (a *PGPGenArg) CreatePGPIDs() error {
   142  	if len(a.Ids) > 0 {
   143  		return nil
   144  	}
   145  	for _, id := range a.PGPUids {
   146  		if !strings.Contains(id, "<") && CheckEmail.F(id) {
   147  			a.Ids = append(a.Ids, Identity{Email: id})
   148  			continue
   149  		}
   150  		parsed, err := ParseIdentity(id)
   151  		if err != nil {
   152  			return err
   153  		}
   154  		a.Ids = append(a.Ids, *parsed)
   155  	}
   156  	return nil
   157  }
   158  
   159  // Just for testing
   160  func (a *PGPGenArg) AddDefaultUID(g *GlobalContext) {
   161  	a.Ids = append(a.Ids, KeybaseIdentity(g, ""))
   162  }
   163  
   164  // Just for testing
   165  func (a *PGPGenArg) MakeAllIds(g *GlobalContext) error {
   166  	if err := a.CreatePGPIDs(); err != nil {
   167  		return err
   168  	}
   169  	a.AddDefaultUID(g)
   170  	return nil
   171  }
   172  
   173  func (a *PGPGenArg) PGPUserIDs() ([]*packet.UserId, error) {
   174  	uids := make([]*packet.UserId, len(a.Ids))
   175  	for i, id := range a.Ids {
   176  		uids[i] = id.ToPGPUserID()
   177  		if uids[i] == nil {
   178  			return nil, fmt.Errorf("Id[%d] failed to convert to PGPUserId (%+v)", i, id)
   179  		}
   180  	}
   181  	return uids, nil
   182  }
   183  
   184  func (a *PGPGenArg) Init() (err error) {
   185  	defBits := 4096
   186  	if a.PrimaryBits == 0 {
   187  		a.PrimaryBits = defBits
   188  	}
   189  	if a.SubkeyBits == 0 {
   190  		a.SubkeyBits = defBits
   191  	}
   192  	if a.PrimaryLifetime == 0 {
   193  		a.PrimaryLifetime = KeyExpireIn
   194  	}
   195  	if a.SubkeyLifetime == 0 {
   196  		a.SubkeyLifetime = SubkeyExpireIn
   197  	}
   198  	return
   199  }
   200  
   201  func (a *PGPGenArg) PreferredSymmetric() []uint8 {
   202  	return []uint8{
   203  		uint8(packet.CipherAES128),
   204  		uint8(packet.CipherAES256),
   205  		uint8(packet.CipherCAST5),
   206  	}
   207  }
   208  
   209  func (a *PGPGenArg) PreferredHash() []uint8 {
   210  	gohash := []crypto.Hash{
   211  		crypto.SHA256,
   212  		crypto.SHA512,
   213  		crypto.SHA1,
   214  		crypto.RIPEMD160,
   215  	}
   216  	var res []uint8
   217  	for _, h := range gohash {
   218  		id, ok := s2k.HashToHashId(h)
   219  		if !ok {
   220  			continue
   221  		}
   222  		res = append(res, id)
   223  	}
   224  	return res
   225  }
   226  
   227  func (a *PGPGenArg) PreferredCompression() []uint8 {
   228  	return []uint8{
   229  		uint8(packet.CompressionNone),
   230  		uint8(packet.CompressionZIP),
   231  		uint8(packet.CompressionZLIB),
   232  	}
   233  }