diff options
| -rw-r--r-- | cmd/taler-auditor-offline-signing/main.go | 159 | 
1 files changed, 133 insertions, 26 deletions
| diff --git a/cmd/taler-auditor-offline-signing/main.go b/cmd/taler-auditor-offline-signing/main.go index 92db7b2..dfbfd0c 100644 --- a/cmd/taler-auditor-offline-signing/main.go +++ b/cmd/taler-auditor-offline-signing/main.go @@ -34,6 +34,8 @@ import (  	"strconv"  ) +var be = binary.BigEndian +  type Input struct {  	Operation string `json:"operation"`  	Arguments struct { @@ -106,15 +108,12 @@ func (ep *EdDSAPublicKey) MarshalJSON() ([]byte, error) {  	return buf, nil  } -const ( -	CURRENCY_LEN     = 12 -	CURRENCY_LEN_STR = "12" -) +const CURRENCY_LEN = 12  type Amount struct { -	Value    uint64 `json:"value"` -	Fraction uint32 `json:"fraction"` -	Currency string `json:"currency"` +	Value    uint64             `json:"value"` +	Fraction uint32             `json:"fraction"` +	Currency [CURRENCY_LEN]byte `json:"currency"`  }  // Amount comes in as something like "CHF:0.32" @@ -129,7 +128,7 @@ func (a *Amount) UnmarshalJSON(in []byte) (e error) {  	} else if len(parts[0]) >= CURRENCY_LEN || nonAllowedCharsRX.Match(parts[0]) {  		return fmt.Errorf("invalid currency")  	} -	a.Currency = string(parts[0]) +	copy(a.Currency[:], parts[0][:])  	// split and parse Value  	parts = bytes.Split(parts[1], []byte(".")) @@ -145,16 +144,47 @@ func (a *Amount) UnmarshalJSON(in []byte) (e error) {  	if len(parts) == 2 {  		if len(parts[1]) == 0 || len(parts[1]) > 8 {  			return fmt.Errorf("invalid fraction") -		} else if v, e := strconv.ParseUint(string(parts[1]), 10, 32); e != nil { +		} else if a.Fraction, e = parseFraction(parts[1]); e != nil {  			return e -		} else { -			a.Fraction = uint32(v)  		}  	}  	return nil  } +// following exchange/src/util/amount.c TALER_string_to_amount() +func parseFraction(input []byte) (uint32, error) { +	const TALER_AMOUNT_FRAC_BASE = 100_000_000 + +	b := uint32(TALER_AMOUNT_FRAC_BASE / 10) + +	var f uint32 + +	for _, c := range input { +		if b == 0 { +			return 0, fmt.Errorf("fractional value too small") +		} + +		if c < '0' || c > '9' { +			return 0, fmt.Errorf("invalid fractional value") +		} + +		f += uint32(c-'0') * b +		b /= 10 +	} + +	return f, nil + +} + +func (a *Amount) Binary() []byte { +	buf := make([]byte, 8+4+CURRENCY_LEN) +	be.PutUint64(buf, a.Value) +	be.PutUint32(buf[8:], a.Fraction) +	copy(buf[8+4:], a.Currency[:CURRENCY_LEN]) +	return buf +} +  type EdDSASignature struct {  	R []byte `json:"r"`  	S []byte `json:"s"` @@ -399,10 +429,10 @@ type SignOperation struct {  	} `json:"arguments"`  } -type SHA512Hash []byte +type SHA512Hash [sha512.Size]byte  func (h *SHA512Hash) MarshalJSON() ([]byte, error) { -	enc, err := crockfordEncode(*h) +	enc, err := crockfordEncode((*h)[:])  	if err != nil {  		return nil, fmt.Errorf("error encoding %v: %e\n", h, err)  	} @@ -415,7 +445,95 @@ func (h *SHA512Hash) MarshalJSON() ([]byte, error) {  	return b, nil  } -var keyfile = flag.String("key", "", "filename of EC25519 private key") +func auditorSignDenom(denom *DenomKey, ahash SHA512Hash, master *EdDSAPublicKey, pk *ed25519.PrivateKey) (SHA512Hash, EdDSASignature) { + +	const TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS = 1064 + +	/* +		We write a bigendian encoded version of ExchangeKeyValidityPS. + +		type Purpose uint32 +		type ExchangeKeyValidityPS struct { +			size             uint32 +			purpose          Purpose +			auditor_url_hash SHA512Hash +			master           EdDSAPublicKey +			start            AbsoluteTime +			expireWithdraw   AbsoluteTime +			expireDeposit    AbsoluteTime +			expireLegal      AbsoluteTime +			value            Amount +			feeWithdraw      Amount +			feeDeposit       Amount +			feeRefresh       Amount +			feeRefund        Amount +			denomHash        SHA512Hash +		} +	*/ + +	size := 4 + // size +		4 + // purpose +		sha512.Size + // auditor_url_hash +		ed25519.PublicKeySize + // master +		4*8 + // start and expire* +		5*(8+4+len(denom.Value.Currency)) + // value and fee* +		sha512.Size // denomHash +	buf := make([]byte, size) + +	n := 0 +	be.PutUint32(buf[n:], uint32(size)) +	n += 4 +	be.PutUint32(buf[n:], TALER_SIGNATURE_AUDITOR_EXCHANGE_KEYS) +	n += 4 +	copy(buf[n:], ahash[:]) +	n += len(ahash) +	copy(buf[n:], *master) +	n += len(*master) +	for _, v := range []uint64{ +		denom.StampStart.TMs, +		denom.StampExpireWithdraw.TMs, +		denom.StampExpireDeposit.TMs, +		denom.StampExpireLegal.TMs, +	} { +		be.PutUint64(buf[n:], v*1000) // milli -> micro +		n += 8 +	} +	for _, v := range [][]byte{ +		denom.Value.Binary(), +		denom.FeeWithdraw.Binary(), +		denom.FeeDeposit.Binary(), +		denom.FeeRefresh.Binary(), +		denom.FeeRefund.Binary(), +	} { +		copy(buf[n:], v) +		n += len(v) +	} +	bin := denom.DenomPub.Binary() +	hash := sha512.Sum512(bin) +	copy(buf[n:], hash[:]) + +	sig := ed25519.Sign(*pk, buf) +	return hash, EdDSASignature{R: sig[:32], S: sig[32:]} +} + +func auditorSign(input *Input, url string, pk ed25519.PrivateKey) []SignOperation { + +	output := make([]SignOperation, len(input.Arguments.Denoms)) +	for i, denom := range input.Arguments.Denoms { +		output[i].Operation = "auditor-sign-denomination-0" + +		hash, sig := auditorSignDenom(&denom, sha512.Sum512(append([]byte(url), 0)), &input.Arguments.MasterPublicKey, &pk) +		output[i].Arguments.HDenumPub = hash +		output[i].Arguments.AuditorSig = sig +	} + +	return output +} + +var ( +	keyfile = flag.String("key", "auditor.key", "filename of EC25519 private key") +	url     = flag.String("url", "https://auditor.codeblau.de/", "auditor url") +)  func main() { @@ -441,18 +559,7 @@ func main() {  	}  	// TODO: here needs to go the call to TALER_exchange_offline_denom_validity_verify - -	output := make([]SignOperation, len(input.Arguments.Denoms)) -	for i, denom := range input.Arguments.Denoms { -		output[i].Operation = "auditor-sign-denomination-0" -		bin := denom.DenomPub.Binary() -		sum := sha512.Sum512(bin) -		output[i].Arguments.HDenumPub = sum[:] - -		// TODO: here needs to go the call to TALER_auditor_denom_validity_sign -		sig := ed25519.Sign(pk, sum[:]) -		output[i].Arguments.AuditorSig = EdDSASignature{R: sig[:32], S: sig[32:]} -	} +	output := auditorSign(input, *url, pk)  	enc := json.NewEncoder(os.Stdout)  	enc.SetIndent("", "  ") | 
