Files
varsig/constant.go
Michael Muré 5cea53af26 perf(constants): avoid allocating a map for each Decode*() call
```
goos: linux
goarch: amd64
pkg: github.com/ucan-wg/go-varsig
cpu: 13th Gen Intel(R) Core(TM) i7-1360P
                         │    old.txt    │               new.txt                │
                         │    sec/op     │    sec/op     vs base                │
DecodeHashAlgorithm-16     145.65n ± 10%   59.62n ± 30%  -59.07% (p=0.000 n=10)
DecodePayloadEncoding-16   136.65n ± 46%   50.02n ±  5%  -63.40% (p=0.000 n=10)
geomean                     141.1n         54.60n        -61.29%

                         │  old.txt   │               new.txt               │
                         │    B/op    │    B/op     vs base                 │
DecodeHashAlgorithm-16     48.00 ± 0%   48.00 ± 0%       ~ (p=1.000 n=10) ¹
DecodePayloadEncoding-16   48.00 ± 0%   48.00 ± 0%       ~ (p=1.000 n=10) ¹
geomean                    48.00        48.00       +0.00%
¹ all samples are equal

                         │  old.txt   │               new.txt               │
                         │ allocs/op  │ allocs/op   vs base                 │
DecodeHashAlgorithm-16     1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=10) ¹
DecodePayloadEncoding-16   1.000 ± 0%   1.000 ± 0%       ~ (p=1.000 n=10) ¹
geomean                    1.000        1.000       +0.00%
¹ all samples are equal
```
2025-07-08 16:53:40 +02:00

127 lines
4.4 KiB
Go

package varsig
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/multiformats/go-multicodec"
)
// Prefix is the multicodec.Code for the varsig's varuint prefix byte.
const Prefix = uint64(multicodec.Varsig)
// HashAlgorithm is the multicodec.Code that specifies the hash algorithm
// that's used to reduce the signed content
type HashAlgorithm uint64
// Constant multicodec.Code values that allow Varsig implementations to
// specify how the payload content is hashed before the signature is
// generated.
const (
HashAlgorithmUnspecified HashAlgorithm = 0x00
HashAlgorithmSHA256 = HashAlgorithm(multicodec.Sha2_256)
HashAlgorithmSHA384 = HashAlgorithm(multicodec.Sha2_384)
HashAlgorithmSHA512 = HashAlgorithm(multicodec.Sha2_512)
HashAlgorithmShake256 = HashAlgorithm(multicodec.Shake256)
)
// DecodeHashAlgorithm reads and validates the expected hash algorithm
// (for varsig types include a variable hash algorithm.)
func DecodeHashAlgorithm(r *bytes.Reader) (HashAlgorithm, error) {
u, err := binary.ReadUvarint(r)
if err != nil {
return HashAlgorithmUnspecified, fmt.Errorf("%w: %w", ErrUnknownHashAlgorithm, err)
}
h := HashAlgorithm(u)
switch h {
case HashAlgorithmSHA256,
HashAlgorithmSHA384,
HashAlgorithmSHA512,
HashAlgorithmShake256:
return h, nil
default:
return HashAlgorithmUnspecified, fmt.Errorf("%w: %x", ErrUnknownHashAlgorithm, h)
}
}
// PayloadEncoding specifies the encoding of the data being (hashed and)
// signed. A canonical representation of the data is required to produce
// consistent hashes and signatures.
type PayloadEncoding uint64
// Constant multicodec.Code values that allow Varsig implementations to
// specify how the payload content is encoded before being hashed. In
// varsig >= v1, only canonical encoding is allowed.
const (
PayloadEncodingUnspecified PayloadEncoding = 0x00
PayloadEncodingVerbatim PayloadEncoding = 0x5f
PayloadEncodingDAGPB = PayloadEncoding(multicodec.DagPb)
PayloadEncodingDAGCBOR = PayloadEncoding(multicodec.DagCbor)
PayloadEncodingDAGJSON = PayloadEncoding(multicodec.DagJson)
PayloadEncodingEIP191 = PayloadEncoding(multicodec.Eip191)
PayloadEncodingJWT PayloadEncoding = 0x6a77
)
// DecodePayloadEncoding reads and validates the expected canonical payload
// encoding of the data to be signed.
func DecodePayloadEncoding(r *bytes.Reader, vers Version) (PayloadEncoding, error) {
u, err := binary.ReadUvarint(r)
if err != nil {
return PayloadEncodingUnspecified, fmt.Errorf("%w: %w", ErrUnsupportedPayloadEncoding, err)
}
payEnc := PayloadEncoding(u)
switch vers {
case Version0:
return decodeEncodingInfoV0(payEnc)
case Version1:
return decodeEncodingInfoV1(payEnc)
default:
return 0, ErrUnsupportedVersion
}
}
// https://github.com/ChainAgnostic/varsig#4-payload-encoding
func decodeEncodingInfoV0(payEnc PayloadEncoding) (PayloadEncoding, error) {
switch payEnc {
case PayloadEncodingVerbatim,
PayloadEncodingDAGPB,
PayloadEncodingDAGCBOR,
PayloadEncodingDAGJSON,
PayloadEncodingJWT,
PayloadEncodingEIP191:
return payEnc, nil
default:
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, Version0, payEnc)
}
}
// https://github.com/expede/varsig/blob/main/README.md#payload-encoding
func decodeEncodingInfoV1(payEnc PayloadEncoding) (PayloadEncoding, error) {
switch payEnc {
case PayloadEncodingVerbatim,
PayloadEncodingDAGCBOR,
PayloadEncodingDAGJSON,
PayloadEncodingEIP191:
return payEnc, nil
default:
return PayloadEncodingUnspecified, fmt.Errorf("%w: version=%d, encoding=%x", ErrUnsupportedPayloadEncoding, Version1, payEnc)
}
}
// Discriminator is (usually) the multicodec.Code representing the public
// key type of the algorithm used to create the signature.
//
// There is not set list of constants here, nor is there a decode function
// as the author of an implementation should include the constant with the
// implementation, and the decoding is handled by the Handler, which uses
// the Discriminator to choose the correct implementation. Also note that
// some of the Discriminator values for a specific implementation have
// changed between varsig v0 and v1, so it's possible to have more than one
// constant defined per implementation.
type Discriminator uint64