package delegation import ( "io" "github.com/ipfs/go-cid" "github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime/codec" "github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/ipld/go-ipld-prime/codec/dagjson" "github.com/ipld/go-ipld-prime/datamodel" "github.com/libp2p/go-libp2p/core/crypto" "github.com/ucan-wg/go-ucan/did" "github.com/ucan-wg/go-ucan/tokens/internal/envelope" ) // ToSealed wraps the delegation token in an envelope, generates the // signature, encodes the result to DAG-CBOR and calculates the CID of // the resulting binary data. func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) { data, err := t.ToDagCbor(privKey) if err != nil { return nil, cid.Undef, err } id, err := envelope.CIDFromBytes(data) if err != nil { return nil, cid.Undef, err } return data, id, nil } // ToSealedWriter is the same as Seal but accepts an io.Writer. func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) { cidWriter := envelope.NewCIDWriter(w) if err := t.ToDagCborWriter(cidWriter, privKey); err != nil { return cid.Undef, err } return cidWriter.CID() } // FromSealed decodes the provided binary data from the DAG-CBOR format, // verifies that the envelope's signature is correct based on the public // key taken from the issuer (iss) field and calculates the CID of the // incoming data. func FromSealed(data []byte) (*Token, error) { tkn, err := FromDagCbor(data) if err != nil { return nil, err } id, err := envelope.CIDFromBytes(data) if err != nil { return nil, err } tkn.cid = id return tkn, nil } // FromSealedReader is the same as Unseal but accepts an io.Reader. func FromSealedReader(r io.Reader) (*Token, error) { cidReader := envelope.NewCIDReader(r) tkn, err := FromDagCborReader(cidReader) if err != nil { return nil, err } id, err := cidReader.CID() if err != nil { return nil, err } tkn.cid = id return tkn, nil } // Encode marshals a View to the format specified by the provided // codec.Encoder. func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) { node, err := t.toIPLD(privKey) if err != nil { return nil, err } return ipld.Encode(node, encFn) } // EncodeWriter is the same as Encode but accepts an io.Writer. func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error { node, err := t.toIPLD(privKey) if err != nil { return err } return ipld.EncodeStreaming(w, node, encFn) } // ToDagCbor marshals the View to the DAG-CBOR format. func (t *Token) ToDagCbor(privKey crypto.PrivKey) ([]byte, error) { return t.Encode(privKey, dagcbor.Encode) } // ToDagCborWriter is the same as ToDagCbor but it accepts an io.Writer. func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error { return t.EncodeWriter(w, privKey, dagcbor.Encode) } // ToDagJson marshals the View to the DAG-JSON format. func (t *Token) ToDagJson(privKey crypto.PrivKey) ([]byte, error) { return t.Encode(privKey, dagjson.Encode) } // ToDagJsonWriter is the same as ToDagJson but it accepts an io.Writer. func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error { return t.EncodeWriter(w, privKey, dagjson.Encode) } // Decode unmarshals the input data using the format specified by the // provided codec.Decoder into a View. // // An error is returned if the conversion fails, or if the resulting // View is invalid. func Decode(b []byte, decFn codec.Decoder) (*Token, error) { node, err := ipld.Decode(b, decFn) if err != nil { return nil, err } return fromIPLD(node) } // DecodeReader is the same as Decode, but accept an io.Reader. func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) { node, err := ipld.DecodeStreaming(r, decFn) if err != nil { return nil, err } return fromIPLD(node) } // FromDagCbor unmarshals the input data into a View. // // An error is returned if the conversion fails, or if the resulting // View is invalid. func FromDagCbor(data []byte) (*Token, error) { pay, err := envelope.FromDagCbor[*tokenPayloadModel](data) if err != nil { return nil, err } tkn, err := tokenFromModel(*pay) if err != nil { return nil, err } return tkn, err } // FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. func FromDagCborReader(r io.Reader) (*Token, error) { return DecodeReader(r, dagcbor.Decode) } // FromDagJson unmarshals the input data into a View. // // An error is returned if the conversion fails, or if the resulting // View is invalid. func FromDagJson(data []byte) (*Token, error) { return Decode(data, dagjson.Decode) } // FromDagJsonReader is the same as FromDagJson, but accept an io.Reader. func FromDagJsonReader(r io.Reader) (*Token, error) { return DecodeReader(r, dagjson.Decode) } func fromIPLD(node datamodel.Node) (*Token, error) { pay, err := envelope.FromIPLD[*tokenPayloadModel](node) if err != nil { return nil, err } tkn, err := tokenFromModel(*pay) if err != nil { return nil, err } return tkn, err } func (t *Token) toIPLD(privKey crypto.PrivKey) (datamodel.Node, error) { var sub *string if t.subject != did.Undef { s := t.subject.String() sub = &s } pol, err := t.policy.ToIPLD() if err != nil { return nil, err } var nbf *int64 if t.notBefore != nil { u := t.notBefore.Unix() nbf = &u } var exp *int64 if t.expiration != nil { u := t.expiration.Unix() exp = &u } model := &tokenPayloadModel{ Iss: t.issuer.String(), Aud: t.audience.String(), Sub: sub, Cmd: t.command.String(), Pol: pol, Nonce: t.nonce, Meta: *t.meta, Nbf: nbf, Exp: exp, } return envelope.ToIPLD(privKey, model) }