package recordimport (pool)// Envelope contains an arbitrary []byte payload, signed by a libp2p peer.//// Envelopes are signed in the context of a particular "domain", which is a// string specified when creating and verifying the envelope. You must know the// domain string used to produce the envelope in order to verify the signature// and access the payload.typeEnvelopestruct {// The public key that can be used to verify the signature and derive the peer id of the signer. PublicKey crypto.PubKey// A binary identifier that indicates what kind of data is contained in the payload. // TODO(yusef): enforce multicodec prefix PayloadType []byte// The envelope payload. RawPayload []byte// The signature of the domain string :: type hint :: payload. signature []byte// the unmarshalled payload as a Record, cached on first access via the Record accessor method cached Record unmarshalError error unmarshalOnce sync.Once}varErrEmptyDomain = errors.New("envelope domain must not be empty")varErrEmptyPayloadType = errors.New("payloadType must not be empty")varErrInvalidSignature = errors.New("invalid signature or incorrect domain")// Seal marshals the given Record, places the marshaled bytes inside an Envelope,// and signs with the given private key.func ( Record, crypto.PrivKey) (*Envelope, error) { , := .MarshalRecord()if != nil {returnnil, fmt.Errorf("error marshaling record: %v", ) } := .Domain() := .Codec()if == "" {returnnil, ErrEmptyDomain }iflen() == 0 {returnnil, ErrEmptyPayloadType } , := makeUnsigned(, , )if != nil {returnnil, }deferpool.Put() , := .Sign()if != nil {returnnil, }return &Envelope{PublicKey: .GetPublic(),PayloadType: ,RawPayload: ,signature: , }, nil}// ConsumeEnvelope unmarshals a serialized Envelope and validates its// signature using the provided 'domain' string. If validation fails, an error// is returned, along with the unmarshalled envelope, so it can be inspected.//// On success, ConsumeEnvelope returns the Envelope itself, as well as the inner payload,// unmarshalled into a concrete Record type. The actual type of the returned Record depends// on what has been registered for the Envelope's PayloadType (see RegisterType for details).//// You can type assert on the returned Record to convert it to an instance of the concrete// Record type://// envelope, rec, err := ConsumeEnvelope(envelopeBytes, peer.PeerRecordEnvelopeDomain)// if err != nil {// handleError(envelope, err) // envelope may be non-nil, even if errors occur!// return// }// peerRec, ok := rec.(*peer.PeerRecord)// if ok {// doSomethingWithPeerRecord(peerRec)// }//// If the Envelope signature is valid, but no Record type is registered for the Envelope's// PayloadType, ErrPayloadTypeNotRegistered will be returned, along with the Envelope and// a nil Record.func ( []byte, string) ( *Envelope, Record, error) { , := UnmarshalEnvelope()if != nil {returnnil, nil, fmt.Errorf("failed when unmarshalling the envelope: %w", ) } = .validate()if != nil {returnnil, nil, fmt.Errorf("failed to validate envelope: %w", ) } , = .Record()if != nil {returnnil, nil, fmt.Errorf("failed to unmarshal envelope payload: %w", ) }return , , nil}// ConsumeTypedEnvelope unmarshals a serialized Envelope and validates its// signature. If validation fails, an error is returned, along with the unmarshalled// envelope, so it can be inspected.//// Unlike ConsumeEnvelope, ConsumeTypedEnvelope does not try to automatically determine// the type of Record to unmarshal the Envelope's payload into. Instead, the caller provides// a destination Record instance, which will unmarshal the Envelope payload. It is the caller's// responsibility to determine whether the given Record type is able to unmarshal the payload// correctly.//// rec := &MyRecordType{}// envelope, err := ConsumeTypedEnvelope(envelopeBytes, rec)// if err != nil {// handleError(envelope, err)// }// doSomethingWithRecord(rec)//// Important: you MUST check the error value before using the returned Envelope. In some error// cases, including when the envelope signature is invalid, both the Envelope and an error will// be returned. This allows you to inspect the unmarshalled but invalid Envelope. As a result,// you must not assume that any non-nil Envelope returned from this function is valid.func ( []byte, Record) ( *Envelope, error) { , := UnmarshalEnvelope()if != nil {returnnil, fmt.Errorf("failed when unmarshalling the envelope: %w", ) } = .validate(.Domain())if != nil {return , fmt.Errorf("failed to validate envelope: %w", ) } = .UnmarshalRecord(.RawPayload)if != nil {return , fmt.Errorf("failed to unmarshal envelope payload: %w", ) } .cached = return , nil}// UnmarshalEnvelope unmarshals a serialized Envelope protobuf message,// without validating its contents. Most users should use ConsumeEnvelope.func ( []byte) (*Envelope, error) {varpb.Envelopeif := proto.Unmarshal(, &); != nil {returnnil, } , := crypto.PublicKeyFromProto(.PublicKey)if != nil {returnnil, }return &Envelope{PublicKey: ,PayloadType: .PayloadType,RawPayload: .Payload,signature: .Signature, }, nil}// Marshal returns a byte slice containing a serialized protobuf representation// of an Envelope.func ( *Envelope) () ( []byte, error) {deferfunc() { catch.HandlePanic(recover(), &, "libp2p envelope marshal") }() , := crypto.PublicKeyToProto(.PublicKey)if != nil {returnnil, } := pb.Envelope{PublicKey: ,PayloadType: .PayloadType,Payload: .RawPayload,Signature: .signature, }returnproto.Marshal(&)}// Equal returns true if the other Envelope has the same public key,// payload, payload type, and signature. This implies that they were also// created with the same domain string.func ( *Envelope) ( *Envelope) bool {if == nil {return == nil }return .PublicKey.Equals(.PublicKey) &&bytes.Equal(.PayloadType, .PayloadType) &&bytes.Equal(.signature, .signature) &&bytes.Equal(.RawPayload, .RawPayload)}// Record returns the Envelope's payload unmarshalled as a Record.// The concrete type of the returned Record depends on which Record// type was registered for the Envelope's PayloadType - see record.RegisterType.//// Once unmarshalled, the Record is cached for future access.func ( *Envelope) () (Record, error) { .unmarshalOnce.Do(func() {if .cached != nil {return } .cached, .unmarshalError = unmarshalRecordPayload(.PayloadType, .RawPayload) })return .cached, .unmarshalError}// TypedRecord unmarshals the Envelope's payload to the given Record instance.// It is the caller's responsibility to ensure that the Record type is capable// of unmarshalling the Envelope payload. Callers can inspect the Envelope's// PayloadType field to determine the correct type of Record to use.//// This method will always unmarshal the Envelope payload even if a cached record// exists.func ( *Envelope) ( Record) error {return .UnmarshalRecord(.RawPayload)}// validate returns nil if the envelope signature is valid for the given 'domain',// or an error if signature validation fails.func ( *Envelope) ( string) error { , := makeUnsigned(, .PayloadType, .RawPayload)if != nil {return }deferpool.Put() , := .PublicKey.Verify(, .signature)if != nil {returnfmt.Errorf("failed while verifying signature: %w", ) }if ! {returnErrInvalidSignature }returnnil}// makeUnsigned is a helper function that prepares a buffer to sign or verify.// It returns a byte slice from a pool. The caller MUST return this slice to the// pool.func makeUnsigned( string, []byte, []byte) ([]byte, error) {var ( = [][]byte{[]byte(), , }// fields are prefixed with their length as an unsigned varint. we // compute the lengths before allocating the sig buffer, so we know how // much space to add for the lengths = make([][]byte, len()) = 0 )for , := range { := len() [] = varint.ToUvarint(uint64()) += + len([]) } := pool.Get()varintfor , := range { += copy([:], []) += copy([:], ) }return [:], nil}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.