timestamp.go 3.21 KB
Newer Older
1 2 3 4 5 6
package irma

import (
	"crypto/sha256"
	"encoding/asn1"
	"errors"
7
	gobig "math/big"
8 9 10

	"github.com/bwesterb/go-atum"
	"github.com/mhe/gabi"
11
	"github.com/mhe/gabi/big"
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
)

// GetTimestamp GETs a signed timestamp (a signature over the current time and the parameters)
// over the message to be signed, the randomized signatures over the attributes, and the disclosed
// attributes, for in attribute-based signature sessions.
func GetTimestamp(message string, sigs []*big.Int, disclosed [][]*big.Int) (*atum.Timestamp, error) {
	nonce, err := TimestampRequest(message, sigs, disclosed)
	if err != nil {
		return nil, err
	}
	alg := atum.Ed25519
	return atum.SendRequest(TimestampServerURL, atum.Request{
		Nonce:           nonce,
		PreferredSigAlg: &alg,
	})
}

// TimestampRequest computes the nonce to be signed by a timestamp server, given a message to be signed
// in an attribute-based signature session along with the randomized signatures over the attributes
// and the disclosed attributes.
func TimestampRequest(message string, sigs []*big.Int, disclosed [][]*big.Int) ([]byte, error) {
	msgHash := sha256.Sum256([]byte(message))

35 36 37 38 39 40 41 42 43 44 45 46
	// Convert the sigs and disclosed (double) slices to (double) slices of gobig.Int's for asn1
	sigsint := make([]*gobig.Int, len(sigs))
	disclosedint := make([][]*gobig.Int, len(disclosed))
	for i, k := range sigs {
		sigsint[i] = k.Value()
	}
	for i, _ := range disclosed {
		disclosedint[i] = make([]*gobig.Int, len(disclosed[i]))
		for j, k := range disclosed[i] {
			disclosedint[i][j] = k.Value()
		}
	}
47
	bts, err := asn1.Marshal(struct {
48
		Sigs      []*gobig.Int
49
		MsgHash   []byte
50
		Disclosed [][]*gobig.Int
51
	}{
52
		sigsint, msgHash[:], disclosedint,
53 54 55 56 57 58 59 60 61 62 63
	})
	if err != nil {
		return nil, err
	}

	hashed := sha256.Sum256(bts)
	return hashed[:], nil
}

const TimestampServerURL = "https://metrics.privacybydesign.foundation/atum"

64
// Given an SignedMessage, verify the timestamp over the signed message, disclosed attributes,
65
// and rerandomized CL-signatures.
66 67
func (sm *SignedMessage) VerifyTimestamp(message string, conf *Configuration) error {
	if sm.Timestamp.ServerUrl != TimestampServerURL {
68 69 70 71 72 73
		return errors.New("Untrusted timestamp server")
	}

	// Extract the disclosed attributes and randomized CL-signatures from the proofs in order to
	// construct the nonce that should be signed by the timestamp server.
	zero := big.NewInt(0)
74
	size := len(sm.Signature)
75 76
	sigs := make([]*big.Int, size)
	disclosed := make([][]*big.Int, size)
77
	for i, proof := range sm.Signature {
78 79
		proofd := proof.(*gabi.ProofD)
		sigs[i] = proofd.A
80 81 82 83
		ct := MetadataFromInt(proofd.ADisclosed[1], conf).CredentialType()
		if ct == nil {
			return errors.New("Cannot verify timestamp: signature contains attributes from unknown credential type")
		}
84
		attrcount := len(ct.AttributeTypes) + 2 // plus secret key and metadata
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
		disclosed[i] = make([]*big.Int, attrcount)
		for j := 0; j < attrcount; j++ {
			val, ok := proofd.ADisclosed[j]
			if !ok {
				disclosed[i][j] = zero
			} else {
				disclosed[i][j] = val
			}
		}
	}

	bts, err := TimestampRequest(message, sigs, disclosed)
	if err != nil {
		return err
	}
100
	valid, err := sm.Timestamp.Verify(bts)
101 102 103 104 105 106 107 108
	if err != nil {
		return err
	}
	if !valid {
		return errors.New("Timestamp signature invalid")
	}
	return nil
}