Files
ucan/token/delegation/ipld.go

238 lines
6.4 KiB
Go
Raw Normal View History

package delegation
import (
"io"
2025-07-31 17:49:01 +02:00
"github.com/MetaMask/go-did-it"
"github.com/MetaMask/go-did-it/crypto"
"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"
2024-10-01 17:02:49 +02:00
"github.com/ucan-wg/go-ucan/token/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.PrivateKeySigningBytes) ([]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
}
2024-10-01 17:08:57 +02:00
// ToSealedWriter is the same as ToSealed but accepts an io.Writer.
func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) (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.
2025-07-31 17:49:01 +02:00
func FromSealed(data []byte, resolvOpts ...did.ResolutionOption) (*Token, cid.Cid, error) {
tkn, err := FromDagCbor(data, resolvOpts...)
if err != nil {
return nil, cid.Undef, err
}
id, err := envelope.CIDFromBytes(data)
if err != nil {
return nil, cid.Undef, err
}
return tkn, id, nil
}
// FromSealedReader is the same as Unseal but accepts an io.Reader.
2025-07-31 17:49:01 +02:00
func FromSealedReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, cid.Cid, error) {
cidReader := envelope.NewCIDReader(r)
2025-07-31 17:49:01 +02:00
tkn, err := FromDagCborReader(cidReader, resolvOpts...)
if err != nil {
return nil, cid.Undef, err
}
id, err := cidReader.CID()
if err != nil {
return nil, cid.Undef, err
}
return tkn, id, nil
}
2024-10-01 13:23:37 +02:00
// Encode marshals a Token to the format specified by the provided
// codec.Encoder.
func (t *Token) Encode(privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) ([]byte, error) {
node, err := t.toIPLD(privKey)
if err != nil {
return nil, err
}
return ipld.Encode(node, encFn)
}
2024-10-01 17:08:57 +02:00
// EncodeWriter is the same as Encode, but accepts an io.Writer.
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) error {
node, err := t.toIPLD(privKey)
if err != nil {
return err
}
return ipld.EncodeStreaming(w, node, encFn)
}
2024-10-01 13:23:37 +02:00
// ToDagCbor marshals the Token to the DAG-CBOR format.
func (t *Token) ToDagCbor(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
return t.Encode(privKey, dagcbor.Encode)
}
2024-10-01 17:08:57 +02:00
// ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer.
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
return t.EncodeWriter(w, privKey, dagcbor.Encode)
}
2024-10-01 13:23:37 +02:00
// ToDagJson marshals the Token to the DAG-JSON format.
func (t *Token) ToDagJson(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
return t.Encode(privKey, dagjson.Encode)
}
2024-10-01 17:08:57 +02:00
// ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer.
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
return t.EncodeWriter(w, privKey, dagjson.Encode)
}
// Decode unmarshals the input data using the format specified by the
2024-10-01 13:23:37 +02:00
// provided codec.Decoder into a Token.
//
// An error is returned if the conversion fails, or if the resulting
2024-10-01 13:23:37 +02:00
// Token is invalid.
2025-07-31 17:49:01 +02:00
func Decode(b []byte, decFn codec.Decoder, resolvOpts ...did.ResolutionOption) (*Token, error) {
node, err := ipld.Decode(b, decFn)
if err != nil {
return nil, err
}
2025-07-31 17:49:01 +02:00
return FromIPLD(node, resolvOpts...)
}
// DecodeReader is the same as Decode, but accept an io.Reader.
2025-07-31 17:49:01 +02:00
func DecodeReader(r io.Reader, decFn codec.Decoder, resolvOpts ...did.ResolutionOption) (*Token, error) {
node, err := ipld.DecodeStreaming(r, decFn)
if err != nil {
return nil, err
}
2025-07-31 17:49:01 +02:00
return FromIPLD(node, resolvOpts...)
}
2024-10-01 13:23:37 +02:00
// FromDagCbor unmarshals the input data into a Token.
//
// An error is returned if the conversion fails, or if the resulting
2024-10-01 13:23:37 +02:00
// Token is invalid.
2025-07-31 17:49:01 +02:00
func FromDagCbor(data []byte, resolvOpts ...did.ResolutionOption) (*Token, error) {
pay, err := envelope.FromDagCbor[*tokenPayloadModel](data, resolvOpts...)
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.
2025-07-31 17:49:01 +02:00
func FromDagCborReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, error) {
return DecodeReader(r, dagcbor.Decode, resolvOpts...)
}
2024-10-01 13:23:37 +02:00
// FromDagJson unmarshals the input data into a Token.
//
// An error is returned if the conversion fails, or if the resulting
2024-10-01 13:23:37 +02:00
// Token is invalid.
2025-07-31 17:49:01 +02:00
func FromDagJson(data []byte, resolvOpts ...did.ResolutionOption) (*Token, error) {
return Decode(data, dagjson.Decode, resolvOpts...)
}
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
2025-07-31 17:49:01 +02:00
func FromDagJsonReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, error) {
return DecodeReader(r, dagjson.Decode, resolvOpts...)
}
2024-10-01 13:23:37 +02:00
// FromIPLD decode the given IPLD representation into a Token.
2025-07-31 17:49:01 +02:00
func FromIPLD(node datamodel.Node, resolvOpts ...did.ResolutionOption) (*Token, error) {
pay, err := envelope.FromIPLD[*tokenPayloadModel](node, resolvOpts...)
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.PrivateKeySigningBytes) (datamodel.Node, error) {
var sub *string
if t.subject != nil {
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,
2024-10-24 13:43:52 -04:00
Meta: t.meta,
Nbf: nbf,
Exp: exp,
}
2024-11-06 16:43:57 +01:00
// seems like it's a requirement to have a null meta if there are no values?
if len(model.Meta.Keys) == 0 {
model.Meta = nil
}
return envelope.ToIPLD(privKey, model)
}