From 98f1e7ef3dbecbe6cfdfdc74a551e988c8a0cfde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sat, 23 Oct 2021 16:47:48 +0200 Subject: [PATCH] signing operation works. PoC quality --- cmd/taler-auditor-offline-signing/main.go | 159 ++++++++++++++++++---- 1 file 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("", " ")