key_package.go raw

   1  package mls
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"io"
   7  	"time"
   8  
   9  	"golang.org/x/crypto/cryptobyte"
  10  )
  11  
  12  // A KeyPackage provides some public information about a user, such as
  13  // a supported protocol version and cipher suite, public keys, and credentials.
  14  //
  15  // Key packages should not be used more than once.
  16  type KeyPackage struct {
  17  	version     protocolVersion
  18  	cipherSuite CipherSuite
  19  	initKey     hpkePublicKey
  20  	leafNode    leafNode
  21  	extensions  []extension
  22  	signature   []byte
  23  }
  24  
  25  // UnmarshalKeyPackage reads a key package encoded as an MLS message.
  26  func UnmarshalKeyPackage(raw []byte) (*KeyPackage, error) {
  27  	var msg mlsMessage
  28  	if err := unmarshal(raw, &msg); err != nil {
  29  		return nil, err
  30  	} else if msg.wireFormat != wireFormatMLSKeyPackage {
  31  		return nil, fmt.Errorf("mls: expected a key package message, got wire format %v", msg.wireFormat)
  32  	}
  33  	return msg.keyPackage, nil
  34  }
  35  
  36  // Bytes encodes the key package wrapped in an MLSMessage envelope
  37  // (version + wireFormat + keyPackage). For the bare TLS serialization
  38  // without the envelope, use RawBytes().
  39  func (pkg *KeyPackage) Bytes() []byte {
  40  	raw, err := marshal(&mlsMessage{
  41  		version:    protocolVersionMLS10,
  42  		wireFormat: wireFormatMLSKeyPackage,
  43  		keyPackage: pkg,
  44  	})
  45  	if err != nil {
  46  		// should never happen
  47  		panic(fmt.Errorf("mls: failed to marshal key package message: %v", err))
  48  	}
  49  	return raw
  50  }
  51  
  52  // RawBytes encodes the key package as bare TLS bytes without the MLSMessage
  53  // envelope. This is the format used by NIP-EE (kind 443 events).
  54  func (pkg *KeyPackage) RawBytes() []byte {
  55  	var b cryptobyte.Builder
  56  	pkg.marshal(&b)
  57  	raw, err := b.Bytes()
  58  	if err != nil {
  59  		panic(fmt.Errorf("mls: failed to marshal raw key package: %v", err))
  60  	}
  61  	return raw
  62  }
  63  
  64  // UnmarshalRawKeyPackage reads a key package from bare TLS bytes (no MLSMessage
  65  // envelope). This is the format used by NIP-EE (kind 443 events).
  66  func UnmarshalRawKeyPackage(raw []byte) (*KeyPackage, error) {
  67  	s := cryptobyte.String(raw)
  68  	pkg := new(KeyPackage)
  69  	if err := pkg.unmarshal(&s); err != nil {
  70  		return nil, err
  71  	}
  72  	if !s.Empty() {
  73  		return nil, fmt.Errorf("mls: raw key package contains %d excess bytes", len(s))
  74  	}
  75  	return pkg, nil
  76  }
  77  
  78  func (pkg *KeyPackage) unmarshal(s *cryptobyte.String) error {
  79  	*pkg = KeyPackage{}
  80  
  81  	ok := s.ReadUint16((*uint16)(&pkg.version)) &&
  82  		s.ReadUint16((*uint16)(&pkg.cipherSuite)) &&
  83  		readOpaqueVec(s, (*[]byte)(&pkg.initKey))
  84  	if !ok {
  85  		return io.ErrUnexpectedEOF
  86  	}
  87  
  88  	if pkg.version != protocolVersionMLS10 {
  89  		return fmt.Errorf("mls: invalid protocol version %d", pkg.version)
  90  	}
  91  
  92  	if err := pkg.leafNode.unmarshal(s); err != nil {
  93  		return err
  94  	}
  95  
  96  	exts, err := unmarshalExtensionVec(s)
  97  	if err != nil {
  98  		return err
  99  	}
 100  	pkg.extensions = exts
 101  
 102  	if !readOpaqueVec(s, &pkg.signature) {
 103  		return err
 104  	}
 105  
 106  	return nil
 107  }
 108  
 109  func (pkg *KeyPackage) marshalTBS(b *cryptobyte.Builder) {
 110  	b.AddUint16(uint16(pkg.version))
 111  	b.AddUint16(uint16(pkg.cipherSuite))
 112  	writeOpaqueVec(b, []byte(pkg.initKey))
 113  	pkg.leafNode.marshal(b)
 114  	marshalExtensionVec(b, pkg.extensions)
 115  }
 116  
 117  func (pkg *KeyPackage) marshal(b *cryptobyte.Builder) {
 118  	pkg.marshalTBS(b)
 119  	writeOpaqueVec(b, pkg.signature)
 120  }
 121  
 122  func (pkg *KeyPackage) sign(signerPriv signaturePrivateKey) error {
 123  	var b cryptobyte.Builder
 124  	pkg.marshalTBS(&b)
 125  	rawTBS, err := b.Bytes()
 126  	if err != nil {
 127  		return err
 128  	}
 129  
 130  	sig, err := pkg.cipherSuite.signWithLabel(signerPriv, []byte("KeyPackageTBS"), rawTBS)
 131  	if err != nil {
 132  		return err
 133  	}
 134  
 135  	pkg.signature = sig
 136  	return nil
 137  }
 138  
 139  func (pkg *KeyPackage) verifySignature() bool {
 140  	var b cryptobyte.Builder
 141  	pkg.marshalTBS(&b)
 142  	rawTBS, err := b.Bytes()
 143  	if err != nil {
 144  		return false
 145  	}
 146  
 147  	return pkg.cipherSuite.verifyWithLabel(pkg.leafNode.signatureKey, []byte("KeyPackageTBS"), rawTBS, pkg.signature)
 148  }
 149  
 150  // verify performs KeyPackage verification as described in RFC 9420 section 10.1.
 151  func (pkg *KeyPackage) verify(ctx *groupContext) error {
 152  	if pkg.version != ctx.version {
 153  		return fmt.Errorf("mls: key package version doesn't match group context")
 154  	}
 155  	if pkg.cipherSuite != ctx.cipherSuite {
 156  		return fmt.Errorf("mls: cipher suite doesn't match group context")
 157  	}
 158  	if pkg.leafNode.leafNodeSource != leafNodeSourceKeyPackage {
 159  		return fmt.Errorf("mls: key package contains a leaf node with an invalid source")
 160  	}
 161  	if !pkg.verifySignature() {
 162  		return fmt.Errorf("mls: invalid key package signature")
 163  	}
 164  	if bytes.Equal(pkg.leafNode.encryptionKey, pkg.initKey) {
 165  		return fmt.Errorf("mls: key package encryption key and init key are identical")
 166  	}
 167  	return nil
 168  }
 169  
 170  // GenerateRef generates this key package's reference.
 171  func (pkg *KeyPackage) GenerateRef() (KeyPackageRef, error) {
 172  	var b cryptobyte.Builder
 173  	pkg.marshal(&b)
 174  	raw, err := b.Bytes()
 175  	if err != nil {
 176  		return nil, err
 177  	}
 178  
 179  	hash, err := pkg.cipherSuite.refHash([]byte("MLS 1.0 KeyPackage Reference"), raw)
 180  	if err != nil {
 181  		return nil, err
 182  	}
 183  
 184  	return KeyPackageRef(hash), nil
 185  }
 186  
 187  // KeyPackageRef is a hash uniquely identifying a key package.
 188  type KeyPackageRef []byte
 189  
 190  // Equal checks whether two key package references are equal.
 191  func (ref KeyPackageRef) Equal(other KeyPackageRef) bool {
 192  	return bytes.Equal([]byte(ref), []byte(other))
 193  }
 194  
 195  // PrivateKeyPackage holds private information about a user.
 196  type PrivateKeyPackage struct {
 197  	InitKey       []byte
 198  	EncryptionKey []byte
 199  	SignatureKey  []byte
 200  }
 201  
 202  // KeyPairPackage holds both public and private information about a user.
 203  type KeyPairPackage struct {
 204  	Public  KeyPackage
 205  	Private PrivateKeyPackage
 206  }
 207  
 208  // KeyPackageOptions configures key package generation.
 209  type KeyPackageOptions struct {
 210  	// CapabilityExtensions are extension types to advertise in the leaf
 211  	// node capabilities (e.g., ExtensionTypeLastResort, ExtensionTypeNostrGroupData).
 212  	// RatchetTree is always included.
 213  	CapabilityExtensions []extensionType
 214  
 215  	// LeafExtensions are extensions to include in the leaf node itself
 216  	// (e.g., a LastResort extension with empty data).
 217  	LeafExtensions []extension
 218  
 219  	// KeyPackageExtensions are extensions to include at the key package level.
 220  	KeyPackageExtensions []extension
 221  }
 222  
 223  // GenerateKeyPairPackage generates a new key pair package.
 224  func GenerateKeyPairPackage(cs CipherSuite, credential *Credential) (*KeyPairPackage, error) {
 225  	return GenerateKeyPairPackageWithOptions(cs, credential, nil)
 226  }
 227  
 228  // GenerateKeyPairPackageWithOptions generates a new key pair package with
 229  // custom capabilities and extensions.
 230  func GenerateKeyPairPackageWithOptions(cs CipherSuite, credential *Credential, opts *KeyPackageOptions) (*KeyPairPackage, error) {
 231  	initPub, initPriv, err := cs.generateEncryptionKeyPair()
 232  	if err != nil {
 233  		return nil, err
 234  	}
 235  
 236  	encPub, encPriv, err := cs.generateEncryptionKeyPair()
 237  	if err != nil {
 238  		return nil, err
 239  	}
 240  
 241  	sigPub, sigPriv, err := cs.signatureScheme().GenerateKeyPair()
 242  	if err != nil {
 243  		return nil, err
 244  	}
 245  
 246  	var capExts []extensionType
 247  	var leafExts []extension
 248  	var kpExts []extension
 249  
 250  	if opts != nil {
 251  		capExts = opts.CapabilityExtensions
 252  		leafExts = opts.LeafExtensions
 253  		kpExts = opts.KeyPackageExtensions
 254  	}
 255  	if capExts == nil {
 256  		// Default: only ratchetTree for backwards compat when no opts given
 257  		capExts = []extensionType{extensionTypeRatchetTree}
 258  	}
 259  
 260  	keyPkg := KeyPackage{
 261  		version:     protocolVersionMLS10,
 262  		cipherSuite: cs,
 263  		initKey:     initPub,
 264  		leafNode: leafNode{
 265  			encryptionKey:  encPub,
 266  			signatureKey:   sigPub,
 267  			leafNodeSource: leafNodeSourceKeyPackage,
 268  			credential:     *credential,
 269  			capabilities: capabilities{
 270  				versions:     []protocolVersion{protocolVersionMLS10},
 271  				cipherSuites: []CipherSuite{cs},
 272  				extensions:   capExts,
 273  				proposals:    []proposalType{proposalTypeAdd, proposalTypeUpdate, proposalTypeRemove},
 274  				credentials:  []credentialType{credentialTypeBasic},
 275  			},
 276  			lifetime:   newLifetime(time.Now().Add(-1*time.Hour), time.Now().Add(84*24*time.Hour)),
 277  			extensions: leafExts,
 278  		},
 279  		extensions: kpExts,
 280  	}
 281  
 282  	if err := keyPkg.leafNode.sign(cs, nil, 0, sigPriv); err != nil {
 283  		return nil, fmt.Errorf("failed to sign leaf node: %v", err)
 284  	}
 285  	if err := keyPkg.sign(sigPriv); err != nil {
 286  		return nil, fmt.Errorf("failed to sign key package: %v", err)
 287  	}
 288  
 289  	return &KeyPairPackage{
 290  		Public: keyPkg,
 291  		Private: PrivateKeyPackage{
 292  			InitKey:       initPriv,
 293  			EncryptionKey: encPriv,
 294  			SignatureKey:  sigPriv,
 295  		},
 296  	}, nil
 297  }
 298