221 lines
4.7 KiB
Go
221 lines
4.7 KiB
Go
package opentimestamps
|
|
|
|
import (
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
|
|
"golang.org/x/crypto/ripemd160"
|
|
)
|
|
|
|
const maxResultLength = 4096
|
|
|
|
type (
|
|
unaryMsgOp func(message []byte) ([]byte, error)
|
|
binaryMsgOp func(message, argument []byte) ([]byte, error)
|
|
)
|
|
|
|
// msgAppend returns the concatenation of msg and arg
|
|
func msgAppend(msg, arg []byte) (res []byte, err error) {
|
|
res = append(res, msg...)
|
|
res = append(res, arg...)
|
|
return
|
|
}
|
|
|
|
// msgPrepend returns the concatenation of arg and msg
|
|
func msgPrepend(msg, arg []byte) (res []byte, err error) {
|
|
res = append(res, arg...)
|
|
res = append(res, msg...)
|
|
return
|
|
}
|
|
|
|
// msgReverse returns the reversed msg. Deprecated.
|
|
func msgReverse(msg []byte) ([]byte, error) {
|
|
if len(msg) == 0 {
|
|
return nil, fmt.Errorf("empty input invalid for msgReverse")
|
|
}
|
|
res := make([]byte, len(msg))
|
|
for i, b := range msg {
|
|
res[len(res)-i-1] = b
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func msgHexlify(msg []byte) ([]byte, error) {
|
|
if len(msg) == 0 {
|
|
return nil, fmt.Errorf("empty input invalid for msgHexlify")
|
|
}
|
|
return []byte(hex.EncodeToString(msg)), nil
|
|
}
|
|
|
|
func msgSHA1(msg []byte) ([]byte, error) {
|
|
res := sha1.Sum(msg)
|
|
return res[:], nil
|
|
}
|
|
|
|
func msgRIPEMD160(msg []byte) ([]byte, error) {
|
|
h := ripemd160.New()
|
|
_, err := h.Write(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return h.Sum([]byte{}), nil
|
|
}
|
|
|
|
func msgSHA256(msg []byte) ([]byte, error) {
|
|
res := sha256.Sum256(msg)
|
|
return res[:], nil
|
|
}
|
|
|
|
type opCode interface {
|
|
match(byte) bool
|
|
decode(*deserializationContext) (opCode, error)
|
|
encode(*serializationContext) error
|
|
apply(message []byte) ([]byte, error)
|
|
}
|
|
|
|
type op struct {
|
|
tag byte
|
|
name string
|
|
}
|
|
|
|
func (o op) match(tag byte) bool {
|
|
return o.tag == tag
|
|
}
|
|
|
|
type unaryOp struct {
|
|
op
|
|
msgOp unaryMsgOp
|
|
}
|
|
|
|
func newUnaryOp(tag byte, name string, msgOp unaryMsgOp) *unaryOp {
|
|
return &unaryOp{op{tag: tag, name: name}, msgOp}
|
|
}
|
|
|
|
func (u *unaryOp) String() string {
|
|
return u.name
|
|
}
|
|
|
|
func (u *unaryOp) decode(ctx *deserializationContext) (opCode, error) {
|
|
ret := *u
|
|
return &ret, nil
|
|
}
|
|
|
|
func (u *unaryOp) encode(ctx *serializationContext) error {
|
|
return ctx.writeByte(u.tag)
|
|
}
|
|
|
|
func (u *unaryOp) apply(message []byte) ([]byte, error) {
|
|
return u.msgOp(message)
|
|
}
|
|
|
|
// Crypto operations
|
|
// These are hash ops that define a digest length
|
|
type cryptOp struct {
|
|
unaryOp
|
|
digestLength int
|
|
}
|
|
|
|
func newCryptOp(
|
|
tag byte, name string, msgOp unaryMsgOp, digestLength int,
|
|
) *cryptOp {
|
|
return &cryptOp{
|
|
unaryOp: *newUnaryOp(tag, name, msgOp),
|
|
digestLength: digestLength,
|
|
}
|
|
}
|
|
|
|
func (c *cryptOp) decode(ctx *deserializationContext) (opCode, error) {
|
|
u, err := c.unaryOp.decode(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &cryptOp{*u.(*unaryOp), c.digestLength}, nil
|
|
}
|
|
|
|
// Binary operations
|
|
// We decode an extra varbyte argument and use it in apply()
|
|
|
|
type binaryOp struct {
|
|
op
|
|
msgOp binaryMsgOp
|
|
argument []byte
|
|
}
|
|
|
|
func newBinaryOp(tag byte, name string, msgOp binaryMsgOp) *binaryOp {
|
|
return &binaryOp{
|
|
op: op{tag: tag, name: name},
|
|
msgOp: msgOp,
|
|
argument: nil,
|
|
}
|
|
}
|
|
|
|
func (b *binaryOp) decode(ctx *deserializationContext) (opCode, error) {
|
|
arg, err := ctx.readVarBytes(0, maxResultLength)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(arg) == 0 {
|
|
return nil, fmt.Errorf("empty argument invalid for binaryOp")
|
|
}
|
|
ret := *b
|
|
ret.argument = arg
|
|
return &ret, nil
|
|
}
|
|
|
|
func (b *binaryOp) encode(ctx *serializationContext) error {
|
|
if err := ctx.writeByte(b.tag); err != nil {
|
|
return err
|
|
}
|
|
return ctx.writeVarBytes(b.argument)
|
|
}
|
|
|
|
func (b *binaryOp) apply(message []byte) ([]byte, error) {
|
|
return b.msgOp(message, b.argument)
|
|
}
|
|
|
|
func (b *binaryOp) String() string {
|
|
return fmt.Sprintf("%s %x", b.name, b.argument)
|
|
}
|
|
|
|
var (
|
|
opAppend = newBinaryOp(0xf0, "APPEND", msgAppend)
|
|
opPrepend = newBinaryOp(0xf1, "PREPEND", msgPrepend)
|
|
opReverse = newUnaryOp(0xf2, "REVERSE", msgReverse)
|
|
opHexlify = newUnaryOp(0xf3, "HEXLIFY", msgHexlify)
|
|
opSHA1 = newCryptOp(0x02, "SHA1", msgSHA1, 20)
|
|
opRIPEMD160 = newCryptOp(0x03, "RIPEMD160", msgRIPEMD160, 20)
|
|
opSHA256 = newCryptOp(0x08, "SHA256", msgSHA256, 32)
|
|
)
|
|
|
|
var opCodes []opCode = []opCode{
|
|
opAppend, opPrepend, opReverse, opHexlify, opSHA1, opRIPEMD160,
|
|
opSHA256,
|
|
}
|
|
|
|
func parseOp(ctx *deserializationContext, tag byte) (opCode, error) {
|
|
for _, op := range opCodes {
|
|
if op.match(tag) {
|
|
return op.decode(ctx)
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("could not decode tag %02x", tag)
|
|
}
|
|
|
|
func parseCryptOp(ctx *deserializationContext) (*cryptOp, error) {
|
|
tag, err := ctx.readByte()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
op, err := parseOp(ctx, tag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if cryptOp, ok := op.(*cryptOp); ok {
|
|
return cryptOp, nil
|
|
} else {
|
|
return nil, fmt.Errorf("expected cryptOp, got %#v", op)
|
|
}
|
|
}
|