diff --git a/README.md b/README.md index cf9ea02..9a5c479 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ On testnet: - https://pl-eu.testnet.drand.sh/ - https://testnet0-api.drand.cloudflare.com/ where we have two networks supporting timelock: -- running with a 3 seconds frequency with signatures on G1: `f3827d772c155f95a9fda8901ddd59591a082df5ac6efe3a479ddb1f5eeb202c` -- running with a 3 seconds frequency with signatures on G2: `7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf` +- running with a 3 seconds frequency with signatures on G1: `cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5` Note these are relying on the League of Entropy **Testnet**, which should not be considered secure. You can also spin up a new drand network and run your own, but note that the security guarantees boil down to the trust you have in your network. @@ -98,11 +97,11 @@ If the OUTPUT exists, it will be overwritten. NETWORK defaults to the drand mainnet endpoint https://api.drand.sh/. CHAIN defaults to the chainhash of quicknet: -52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971 +`52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971` You can also use the drand test network: https://pl-us.testnet.drand.sh/ -and its unchained network on G2 with chainhash 7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf +and its unchained network on G1 with chainhash `cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5` Note that if you encrypted something prior to March 2023, this was the only available network and used to be the default. DURATION, when specified, expects a number followed by one of these units: @@ -121,7 +120,7 @@ Files can be encrypted using a duration (`--duration/-D`) in which the `encrypte Example using the testnet network and a duration of 5 seconds: ```bash -$ tle -n="https://pl-us.testnet.drand.sh/" -c="7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf" -D=5s -o=encrypted_data data.txt +$ tle -n="https://pl-us.testnet.drand.sh/" -c="cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5" -D=5s -o=encrypted_data data.txt ``` If a round (`--round/-R`) number is known, it can be used instead of the duration. The data can be decrypted only when that round becomes available in the network. @@ -140,7 +139,7 @@ $ tle -a -D 20s -o=encrypted_data.PEM data.txt #### Timelock Decryption For decryption, it's only necessary to specify the network if you're not using the default one. -Since v1.3.0, some auto-detection of chainhash and network is done upon decryption. +Since v1.2, some auto-detection of chainhash and network is done upon decryption. Using the default values, and printing on stdout: ```bash @@ -149,7 +148,7 @@ $ tle -d encrypted_data Using the old testnet unchained network and storing the output in a file named "decrypted_data": ```bash -$ tle -d -n="https://pl-us.testnet.drand.sh/" -c="7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf" +$ tle -d -n="https://pl-us.testnet.drand.sh/" -c="cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5" -o=decrypted_data encrypted_data ``` Note it will overwrite the `decrypted_data` file if it already exists. diff --git a/cmd/age-plugin-tlock/README.md b/cmd/age-plugin-tlock/README.md new file mode 100644 index 0000000..1080085 --- /dev/null +++ b/cmd/age-plugin-tlock/README.md @@ -0,0 +1,11 @@ +# age-plugin-tlock is a tlock plugin for `age` + +This binary is meant to be put in your PATH so that `age` can rely on it when it encounters either a `age1tlock1` recipient or a `AGE-PLUGIN-TLOCK` identity. + +It allows 3 different types of recipients and identities: +- the static ones, where you're providing all informations such as public key, chainshash, genesis time, period, round value and even signature (for decryption) +- the remote endpoint ones, where you specify which remote endpoint you'd like to rely on and which chainhash you're targeting +- the interactive one where `age` will be prompting you for all necessary informations + +# tlock Identities + diff --git a/cmd/age-plugin-tlock/bincode.go b/cmd/age-plugin-tlock/bincode.go new file mode 100644 index 0000000..620e908 --- /dev/null +++ b/cmd/age-plugin-tlock/bincode.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/binary" + "io" + "log/slog" + "math" +) + +// intEncode re-implements the bincode format for uint64 values +func intEncode(u uint64) []byte { + buf := make([]byte, 1, binary.MaxVarintLen64) + switch { + case u < 251: + buf[0] = byte(u) + case u < math.MaxInt16: + buf[0] = byte(251) + buf = binary.LittleEndian.AppendUint16(buf, uint16(u)) + case u < math.MaxInt32: + buf[0] = byte(252) + buf = binary.LittleEndian.AppendUint32(buf, uint32(u)) + case u < math.MaxInt64: + buf[0] = byte(253) + buf = binary.LittleEndian.AppendUint64(buf, u) + default: + // 254 is meant for u128, but we don't support 128 bit integers here. + buf[0] = byte(254) + } + + return buf +} + +func intDecode(r io.Reader) (uint64, int) { + buf := make([]byte, 1) + i, err := r.Read(buf) + if err != nil || i != 1 { + slog.Error("intDecode error", "error", err, "read", i) + return 0, -1 + } + u := buf[0] + switch { + case int(u) < 251: + return uint64(u), 1 + case u == byte(251): + data := make([]byte, 2) + i, err = r.Read(data) + if err != nil || i <= 0 { + slog.Error("read data error", "error", err, "read", i) + return 0, -1 + } + return uint64(binary.LittleEndian.Uint16(data)), 1 + 2 + case u == byte(252): + data := make([]byte, 4) + i, err = r.Read(data) + if err != nil || i <= 0 { + slog.Error("read data error", "error", err, "read", i) + return 0, -1 + } + return uint64(binary.LittleEndian.Uint32(data)), 1 + 4 + case u == byte(253): + data := make([]byte, 8) + i, err = r.Read(data) + if err != nil || i <= 0 { + slog.Error("read data error", "error", err, "read", i) + return 0, -1 + } + return uint64(binary.LittleEndian.Uint64(data)), 1 + 8 + case u == byte(254): + slog.Error("u128 are unsupported") + return 0, -1 + + } + return 0, -1 +} diff --git a/cmd/age-plugin-tlock/bincode_test.go b/cmd/age-plugin-tlock/bincode_test.go new file mode 100644 index 0000000..1d2289c --- /dev/null +++ b/cmd/age-plugin-tlock/bincode_test.go @@ -0,0 +1,60 @@ +package main + +import ( + "bytes" + "fmt" + "testing" +) + +func Test_intEncode(t *testing.T) { + tests := []struct { + input uint64 + want []byte + }{ + {1677685200, []byte{252, 208, 113, 255, 99}}, + {5, []byte{5}}, + {255, []byte{251, 255, 0}}, + {15266267, []byte{252, 219, 241, 232, 0}}, + {1595431050, []byte{252, 138, 88, 24, 95}}, + {4641203, []byte{252, 179, 209, 70, 0}}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("test-%d", tt.input), func(t *testing.T) { + got := intEncode(tt.input) + t.Log("got", got, "for", tt.input) + if !bytes.Equal(got, tt.want) { + t.Errorf("intEncode() = %v, want %v", got, tt.want) + } + if dec, err := intDecode(bytes.NewReader(got)); err <= 0 || dec != tt.input { + t.Errorf("intDecode() = %v, err %v", dec, err) + } + }) + } +} + +func Test_intDecode(t *testing.T) { + tests := []struct { + want uint64 + input []byte + }{ + {1677685200, []byte{252, 208, 113, 255, 99}}, + {5, []byte{5}}, + {5, []byte{5, 0, 0}}, + {5, []byte{5, 0}}, + {255, []byte{251, 255, 0}}, + {255, []byte{251, 255}}, + {15266267, []byte{252, 219, 241, 232, 0, 0, 0}}, + {1595431050, []byte{252, 138, 88, 24, 95}}, + {4641203, []byte{252, 179, 209, 70, 0}}, + {4641203, []byte{252, 179, 209, 70}}, + } + for _, tt := range tests { + t.Run(fmt.Sprintf("test-%d", tt.input), func(t *testing.T) { + got, n := intDecode(bytes.NewReader(tt.input)) + t.Log("got", got, "for", tt.input) + if got != tt.want || n <= 0 { + t.Errorf("intDecode() = %v, err %v", got, n) + } + }) + } +} diff --git a/cmd/age-plugin-tlock/keygen.go b/cmd/age-plugin-tlock/keygen.go new file mode 100644 index 0000000..d9e6f31 --- /dev/null +++ b/cmd/age-plugin-tlock/keygen.go @@ -0,0 +1,87 @@ +package main + +import ( + "encoding/hex" + "fmt" + "net/url" + + page "filippo.io/age/plugin" +) + +func generateKeypair(p *page.Plugin, args []string, chainhash, remote string) error { + var data []byte + onlyID := false + l := len(args) + switch { + // when user is not providing enough inputs, default to interactive + case l < 3: + fmt.Println("Generating an interactive identity prompting you for details upon use") + + data = append([]byte{0x02}, []byte("interactive")...) + + // when we have a URL, default to HTTP mode, if it's not a URL try to parse it as a signature in hex (and only create an identity) + case l == 3: + host, err := url.Parse(args[l-1]) + if err != nil { + return fmt.Errorf("invalid URL provided in keygen: %w", err) + } + if !host.IsAbs() { + fmt.Println("generating an identity based on the provided signature") + sig, err := hex.DecodeString(args[l-1]) + if err != nil { + return fmt.Errorf("invalid URL/signature provided in keygen: %w", err) + } + + onlyID = true + data = append([]byte{0x00}, sig...) + } else { + fmt.Println("generating a HTTP identity, relying on the network to get data") + + data = append([]byte{0x01}, []byte(host.String())...) + } + // when we have a public key plus a chainhash, we can create a non-interactive, non-http recipient + case l == 4: + fmt.Println("generating a static recipient, containing the public key and chainhash") + + pkb, err := hex.DecodeString(args[2]) + if err != nil { + return fmt.Errorf("invalid public key hex provided in keygen: %w", err) + } + chb, err := hex.DecodeString(args[3]) + if err != nil { + return fmt.Errorf("invalid chainhash hex provided in keygen: %w", err) + } + + data = append([]byte{0x00}, pkb...) + data = append(data, chb...) + default: + Usage() + return nil + } + + // we generate a recipient unless we only want an Identity (e.g. we got a signature instead as input) + if !onlyID { + pub := page.EncodeRecipient(p.Name(), data) + fmt.Println("recipient", pub) + } + + priv := page.EncodeIdentity(p.Name(), data) + fmt.Println("identity", priv) + return nil +} + +func Usage() { + fmt.Println("Usage of age-plugin-tlock:") + fmt.Printf("\t-keygen\n\t\tgenerate age identity and recipient for age-plugin-tlock usage. You have options:\n\t\t" + + "use age in interactive mode, getting prompted for all required data:\n\t\t\t" + + "age-plugin-tlock -keygen\n\t\t" + + "providing a http endpoint (works for both encryption and decryption, but require networking): \n\t\t\t" + + "age-plugin-tlock -keygen http://api.drand.sh/\n\t\t" + + "providing the signature of a given round in hexadecimal, only generates the identity required for decryption with it: \n\t\t\t" + + "age-plugin-tlock -keygen http://api.drand.sh/\n\t\t" + + "providing a public key and a chainhash (requires networking to fetch genesis and period, but is networkless afterwards):\n\t\t\t" + + "age-plugin-tlock -keygen \n\t\t" + + "providing the hexadecimal signature for the round you're interested in (networkless for decryption): \n\t\t\t" + + "age-plugin-tlock -keygen " + + "\n") +} diff --git a/cmd/age-plugin-tlock/main.go b/cmd/age-plugin-tlock/main.go new file mode 100644 index 0000000..db2b407 --- /dev/null +++ b/cmd/age-plugin-tlock/main.go @@ -0,0 +1,352 @@ +package main + +import ( + "bytes" + "encoding/binary" + "encoding/hex" + "errors" + "flag" + "fmt" + "log" + "log/slog" + "os" + "strconv" + "strings" + "time" + + "filippo.io/age" + page "filippo.io/age/plugin" + "github.com/drand/drand/v2/crypto" + "github.com/drand/kyber" + bls "github.com/drand/kyber-bls12381" + "github.com/drand/tlock" + "github.com/drand/tlock/cmd/tle/commands" + "github.com/drand/tlock/networks" + "github.com/drand/tlock/networks/fixed" + "github.com/drand/tlock/networks/http" +) + +// using quicknet by default +var ( + DefaultChainhash = "52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971" + DefaultPK = "83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a" + DefaultPeriod = 3 + DefaultGenesis int64 = 1692803367 + DefaultRemote = "http://api.drand.sh/" + deprecatedFastnet = "dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493" +) + +func main() { + // we set up the flags we need + fs := flag.NewFlagSet("age-plugin-tlock", flag.ExitOnError) + fs.Usage = Usage + isKeyGen := fs.Bool("keygen", false, "Generate a test keypair") + + chainHash := fs.String("chainhash", "", "The chainhash you want to encrypt towards. Default to the 'quicknet' one") + remote := fs.String("remote", "", "The remote endpoint you want to use for getting data. Default to 'https://api.drand.sh'. If using a chainhash, https://api.drand.sh/52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971 will override the default one as well") + + p, err := page.New("tlock") + if err != nil { + slog.Error("error creating plugin", "err", err) + return + } + + p.HandleRecipient(NewRecipient(p)) + p.HandleIdentity(NewIdentity(p)) + // we let age register its required flags + p.RegisterFlags(fs) + + err = fs.Parse(os.Args[1:]) + if err != nil { + log.Fatal(err) + } + + if *isKeyGen { + err := generateKeypair(p, os.Args, *chainHash, *remote) + if err != nil { + log.Fatal("unable to genreate keypair", err) + } + + return + } + + p.Main() +} + +// createRecipient creates data for recipients of the form: +// age1tlock1 +func createRecipient(chainhash []byte, publicKey []byte, genesis int64, period uint, round int64) []byte { + b := bytes.Buffer{} + // we follow the tlock-ts encoding that uses https://github.com/bincode-org/bincode/blob/trunk/docs/spec.md + b.Write(append([]byte{byte(len(chainhash))}, chainhash...)) + b.Write(append([]byte{byte(len(publicKey))}, publicKey...)) + // varint encoding of genesis + b.Write(intEncode(uint64(genesis))) + b.Write(intEncode(uint64(period))) + if round > 0 { + b.Write(intEncode(uint64(round))) + } + return b.Bytes() +} + +func decodePublicKey(pks string) (kyber.Point, *crypto.Scheme, error) { + suite := bls.NewBLS12381Suite() + data, err := hex.DecodeString(pks) + if err != nil { + return nil, nil, err + } + switch l := len(data); l { + case suite.G1().PointLen(): + slog.Debug("detected public key on G1") + var p bls.KyberG1 + if err := p.UnmarshalBinary(data); err != nil { + return nil, nil, fmt.Errorf("unmarshal kyber G1: %w", err) + } + sch := crypto.NewPedersenBLSUnchained() + return &p, sch, nil + case suite.G2().PointLen(): + slog.Debug("detected public key on G2") + var p bls.KyberG2 + if err := p.UnmarshalBinary(data); err != nil { + return nil, nil, fmt.Errorf("unmarshal kyber G2: %w", err) + } + sch := crypto.NewPedersenBLSUnchainedG1() + return &p, sch, nil + default: + } + return nil, nil, errors.New("invalid public key len") +} + +func NewIdentity(p *page.Plugin) func([]byte) (age.Identity, error) { + return func(data []byte) (age.Identity, error) { + if len(data) < 1 { + return nil, errors.New("invalid identity") + } + var sig []byte + var err error + var network networks.Network + switch data[0] { + case 0: + slog.Info("parsed data[0] == 0, RAW mode") + sig = make([]byte, len(data[1:])/2) + n, err := hex.Decode(sig, data[1:]) + if err != nil { + return nil, err + } + if n != len(sig) { + return nil, errors.New("error decoding signature from identity") + } + network, err = fixed.NewNetwork("", nil, nil, 0, 0, sig) + if err != nil { + return nil, err + } + case 1: + slog.Info("parsed data[0] == 1, HTTP mode", "string", string(data[1:])) + network, err = ParseNetworkURL(string(data[1:])) + case 2: + // interactive mode + slog.Info("parsed data[0] == 2, interactive mode") + return interactive{p: p}, nil + default: + slog.Error("unknown tlock identity", "type", data[0]) + slog.Info("defaulting to interactive mode") + return interactive{p: p}, nil + } + // we need to have tlock use the SwitchChainHash on the fixed network for it to work + return tlock.NewIdentity(network, true), err + } +} + +type interactive struct { + p *page.Plugin +} + +type target struct { + round string + chainhash string +} + +func (i interactive) Unwrap(stanzas []*age.Stanza) ([]byte, error) { + fmt.Fprintln(os.Stderr, "starting Unwrap in interactive mode", "#stanzas", len(stanzas)) + var targets []target + for _, s := range stanzas { + if s.Type != "tlock" { + continue + } + + if len(s.Args) != 2 { + continue + } + + target := target{ + round: s.Args[0], + chainhash: s.Args[1], + } + targets = append(targets, target) + } + + if len(targets) != 1 { + return nil, errors.New("tlock only supports a single stanza in interactive mode for now") + } + network, err := i.requestNetwork(targets[0].chainhash, targets[0].round) + if err != nil { + return nil, err + } + + id := tlock.NewIdentity(network, true) + + return id.Unwrap(stanzas) +} + +func (i interactive) requestRound() (uint64, error) { + roundStr, err := i.p.RequestValue("please provide the round number you want to encrypt towards", false) + if err != nil { + return 0, err + } + return strconv.ParseUint(roundStr, 10, 64) +} + +func (i interactive) requestNetwork(chainhash, round string) (networks.Network, error) { + if chainhash == "" { + var err error + chainhash, err = i.p.RequestValue("Please provide the chainhash of the network you want to work with:", false) + if err != nil { + return nil, err + } + if chainhash == "" { + chainhash = DefaultChainhash + } + } + usePK, err := i.p.Confirm("Do you want to provide the group public key and round signature, or do you want to use a HTTP relay?", "use public key", "use HTTP relay") + if err != nil { + return nil, fmt.Errorf("confirmation error in Unwrap: %w", err) + } + if usePK { + pks := DefaultPK + if chainhash != DefaultChainhash { + pks, err = i.p.RequestValue("Please provide the hex encoded public key for the chainhash "+chainhash, false) + if err != nil { + return nil, err + } + } + pk, sch, err := decodePublicKey(pks) + if err != nil { + return nil, err + } + var sig []byte + if round != "" { + sigs, err := i.p.RequestValue("please provide the hex encoded signature of the round "+round, false) + if err != nil { + return nil, err + } + sig, err = hex.DecodeString(sigs) + if err != nil { + return nil, err + } + } + return fixed.NewNetwork(chainhash, pk, sch, 0, 0, sig) + } + + host, err := i.p.RequestValue(fmt.Sprintf("Please provide the http relay for chainhash %q", chainhash), false) + if err != nil { + return nil, err + } + if host == "" { + host = DefaultRemote + } + return http.NewNetwork(host, chainhash) +} + +func (i interactive) Wrap(fileKey []byte) ([]*age.Stanza, error) { + fmt.Fprintln(os.Stderr, "starting Wrap in interactive mode") + net, err := i.requestNetwork("", "") + if err != nil { + return nil, err + } + round, err := i.requestRound() + if err != nil { + return nil, err + } + + rec := tlock.NewRecipient(net, round) + + return rec.Wrap(fileKey) +} + +// NewRecipient parses the recipient from the age1tlock1 recipient strings. +// age1tlock1 +func NewRecipient(p *page.Plugin) func([]byte) (age.Recipient, error) { + return func(data []byte) (age.Recipient, error) { + // RAW mode + var chainhash []byte + var pk kyber.Point + var scheme *crypto.Scheme + offset := 0 + if length := len(data); length >= 32+48+8+8 { + if data[0] != 32 { + return nil, fmt.Errorf("invalid len %d for chainhash", data[0]) + } + chainhash = data[1 : 1+32] + if length >= 1+32+1+1+1+1 && length <= 1+32+1+48+8+8+8 { + pk = new(bls.KyberG1) + offset = 48 + scheme = crypto.NewPedersenBLSUnchained() + } else if length >= 1+32+1+96+1+1+1 && length <= 1+32+1+96+8+8+8 { + pk = new(bls.KyberG2) + offset = 96 + scheme = crypto.NewPedersenBLSUnchainedG1() + // special case to support old ciphertexts using fastnet + if hex.EncodeToString(chainhash) == deprecatedFastnet { + //lint:ignore SA1019 We keep compatibility with older schemes + scheme = crypto.NewPedersenBLSUnchainedSwapped() + } + } else { + return nil, fmt.Errorf("invalid len %d for tlock recipient", length) + } + + // decoding public key + if err := pk.UnmarshalBinary(data[1+32+1 : 1+32+1+offset]); err != nil { + return nil, fmt.Errorf("unmarshal kyber G2: %w", err) + } + + // parsing genesis time + r := bytes.NewReader(data[1+32+1+offset:]) + genesis, err := binary.ReadVarint(r) + if err != nil { + return nil, fmt.Errorf("unable to read genesis: %w", err) + } + period, err := binary.ReadVarint(r) + if err != nil { + return nil, fmt.Errorf("unable to read period: %w", err) + } + round, i := intDecode(r) + if i <= 0 { + slog.Error("invalid round in recipient, aborting") + return nil, fmt.Errorf("wrong round") + } + + network, err := fixed.NewNetwork(hex.EncodeToString(chainhash), pk, scheme, time.Duration(period)*time.Second, genesis, nil) + + return tlock.NewRecipient(network, uint64(round)), err + } else { + slog.Error("invalid length for tlock recipient", "length", length) + slog.Debug("using interactive mode", "data", data) + return interactive{p: p}, nil + } + } +} + +func ParseNetworkURL(u string) (networks.Network, error) { + s := strings.TrimRight(u, "/") + urls := strings.Split(s, "/") + chainhash := urls[len(urls)-1] + if len(chainhash) == 64 { + urls = urls[:len(urls)-1] + fmt.Fprintln(os.Stderr, "using chainhash from endpoint", chainhash) + } else { + fmt.Fprintf(os.Stderr, "unable to parse chainhash %q from URL, using quicknet chainhash", chainhash) + chainhash = commands.DefaultChain + } + + return http.NewNetwork(strings.Join(urls, "/"), chainhash) +} diff --git a/cmd/age-plugin-tlock/main_test.go b/cmd/age-plugin-tlock/main_test.go new file mode 100644 index 0000000..6a209fd --- /dev/null +++ b/cmd/age-plugin-tlock/main_test.go @@ -0,0 +1,156 @@ +//lint:file-ignore SA1019 We want to test even deprecated functions. +package main + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "fmt" + "log/slog" + "os" + "testing" + + "filippo.io/age" + page "filippo.io/age/plugin" + "github.com/drand/drand/v2/common" + "github.com/drand/drand/v2/crypto" + bls "github.com/drand/kyber-bls12381" + "github.com/drand/tlock" + "github.com/drand/tlock/networks/fixed" + "github.com/drand/tlock/networks/http" + "github.com/stretchr/testify/require" +) + +func TestMain(m *testing.M) { + lvl := new(slog.LevelVar) + lvl.Set(slog.LevelDebug) + + logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ + Level: lvl, + })) + + // Set the global logger if needed (depends on your use case) + slog.SetDefault(logger) + + // Run tests + code := m.Run() + + os.Exit(code) // Ensure proper exit after tests +} + +func TestNewIdentity(t *testing.T) { + t.Skip("require internet connectivity") + name, data, err := page.ParseIdentity("AGE-PLUGIN-TLOCK-1Q9TXSAR5WPEN5TE0V9CXJTNYWFSKUEPWWD5Z7ERZVS6NQDNYXEJKVDEKV56KVVECXENRGVTRXC6NZERRVGURQWRRX43XXCNYXU6NGDE3VD3NGETPVESNXE35V3NRWCTYX3JNGCE58YEJ74QEJUM") + slog.Info("ParseIdentity", "name", name, "data", data) + require.NoError(t, err) + require.Equal(t, "tlock", name) + + pkb, err := hex.DecodeString("a0b862a7527fee3a731bcb59280ab6abd62d5c0b6ea03dc4ddf6612fdfc9d01f01c31542541771903475eb1ec6615f8d0df0b8b6dce385811d6dcf8cbefb8759e5e616a3dfd054c928940766d9a5b9db91e3b697e5d70a975181e007f87fca5e") + require.NoError(t, err) + pk := new(bls.KyberG2) + require.NoError(t, pk.UnmarshalBinary(pkb)) + network, err := fixed.NewNetwork("dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493", pk, crypto.NewPedersenBLSUnchainedSwapped(), 0, 0, nil) + require.NoError(t, err) + + tests := []struct { + name string + data []byte + want age.Identity + wantErr bool + }{ + { + name: "empty", + data: []byte{}, + want: nil, + wantErr: true, + }, + { + name: "valid-tlock-rs", + data: data, + want: tlock.NewIdentity(network, true), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Log("testing", tt.name) + got, err := NewIdentity(nil)(tt.data) + if (err != nil) != tt.wantErr { + t.Errorf("NewIdentity() error = %v, wantErr %v", err, tt.wantErr) + return + } + if fmt.Sprintf("%s", got) != fmt.Sprintf("%s", tt.want) { + t.Errorf("NewIdentity() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNewRecipient(t *testing.T) { + // Old deprecated Fastnet recipient + name, data, err := page.ParseRecipient("age1tlock1yrda2pkkaamwtuux7swx28wtszx9hj7h23cucn40506d77k5unzfxc9qhp32w5nlaca8xx7tty5q4d4t6ck4czmw5q7ufh0kvyhaljwsruqux92z2sthryp5wh43a3npt7xsmu9ckmww8pvpr4kulr97lwr4ne0xz63al5z5ey5fgpmxmxjmnku3uwmf0ewhp2t4rq0qqlu8ljj7lng8rlmrqvpvft27") + t.Log(data) + require.NoError(t, err) + require.Equal(t, "tlock", name) + + pkb, _ := hex.DecodeString("a0b862a7527fee3a731bcb59280ab6abd62d5c0b6ea03dc4ddf6612fdfc9d01f01c31542541771903475eb1ec6615f8d0df0b8b6dce385811d6dcf8cbefb8759e5e616a3dfd054c928940766d9a5b9db91e3b697e5d70a975181e007f87fca5e") + pk := new(bls.KyberG2) + require.NoError(t, pk.UnmarshalBinary(pkb)) + + // old fastnet details + network, err := fixed.NewNetwork("dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493", pk, crypto.NewPedersenBLSUnchainedSwapped(), 0, 0, nil) + require.NoError(t, err) + + tests := []struct { + name string + data []byte + want *tlock.Recipient + wantErr bool + }{ + { + "valid", + data, + tlock.NewRecipient(network, 3), + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewRecipient(nil)(tt.data) + if (err != nil) != tt.wantErr { + t.Errorf("NewRecipient() error = %v, wantErr %v", err, tt.wantErr) + return + } + if sgot := fmt.Sprintf("%v", got); sgot == "" || sgot != fmt.Sprintf("%v", tt.want) { + t.Errorf("strings mismatch:\n%v !=\n%v", got, tt.want) + } + }) + } +} + +func TestEncodeRecipient(t *testing.T) { + name, wanted, err := page.ParseRecipient("age1tlock1ypfdhxa8pcxvpah277qrm5r5g7sl23mhxh7n7eshj2afgcqvsn5hzcyreu8j394daelt3d0srl9d8yfzztzr0cq886g3lwgqytf7wcqc8jxyk3gtdg9xcwkx54mk5tgsv3gs68lvwkxfy8xz9v8p0e364a9ukhkkvvzda88cpx7jwn988w454adxa8rk5j7qnemw46yerm67eez6lsnjrenyqvg8g67n") + require.NoError(t, err) + require.Equal(t, "tlock", name) + + pkb, _ := hex.DecodeString("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a") + chainhash, _ := hex.DecodeString("52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971") + + if got := createRecipient(chainhash, pkb, 1692803367, 3, -1); !bytes.Equal(got, wanted) { + t.Errorf("EncodeRecipient() = \t%v,\n\t\t\t\t\t\t\t want \t%v", got, wanted) + } +} + +func TestRealValues(t *testing.T) { + infoJSON := `{"public_key":"83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a","period":3,"genesis_time":1692803367,"genesis_seed":"f477d5c89f21a17c863a7f937c6a6d15859414d2be09cd448d4279af331c5d3e","chain_hash":"52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971","scheme":"bls-unchained-g1-rfc9380","beacon_id":"quicknet"}` + + net, err := http.NewFromJSON(infoJSON) + require.NoError(t, err) + require.NotNil(t, net) + + sigJSON := `{"round":67,"signature":"8a5e6a60b0be2adc46d31a12618cb9bb0584aa90751e7e575582f080dfdb430edf5af11411fe30a786c75c234a67c71e"}` + b := new(common.Beacon) + err = json.Unmarshal([]byte(sigJSON), b) + require.NoError(t, err) + require.NotNil(t, b) +} diff --git a/cmd/tle/commands/commands.go b/cmd/tle/commands/commands.go index f64faf9..1525634 100644 --- a/cmd/tle/commands/commands.go +++ b/cmd/tle/commands/commands.go @@ -49,7 +49,7 @@ CHAIN defaults to the chainhash of quicknet: You can also use the drand test network: https://pl-us.testnet.drand.sh/ -and its unchained network on G2 with chainhash 7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf +and its unchained network on G2 with chainhash cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5 Note that if you encrypted something prior to March 2023, this was the only available network and used to be the default. DURATION, when specified, expects a number followed by one of these units: diff --git a/go.mod b/go.mod index b3be920..25b533c 100644 --- a/go.mod +++ b/go.mod @@ -1,30 +1,28 @@ module github.com/drand/tlock -go 1.23.0 - -toolchain go1.23.2 +go 1.25.0 require ( filippo.io/age v1.2.1 - github.com/drand/drand/v2 v2.1.2 + github.com/drand/drand/v2 v2.1.4 github.com/drand/go-clients v0.2.3 - github.com/drand/kyber v1.3.1 - github.com/drand/kyber-bls12381 v0.3.3 - github.com/stretchr/testify v1.10.0 + github.com/drand/kyber v1.3.2 + github.com/drand/kyber-bls12381 v0.3.4 + github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/klauspost/compress v1.18.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect go.dedis.ch/fixbuf v1.0.3 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 // indirect ) require ( - github.com/BurntSushi/toml v1.5.0 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -33,16 +31,16 @@ require ( github.com/kelseyhightower/envconfig v1.4.0 github.com/kilic/bls12-381 v0.1.0 // indirect github.com/nikkolasg/hexjson v0.1.0 // indirect - github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/prometheus/common v0.67.4 // indirect + github.com/prometheus/procfs v0.19.2 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.38.0 // indirect - golang.org/x/net v0.40.0 // indirect - golang.org/x/sys v0.33.0 // indirect - golang.org/x/text v0.25.0 // indirect - google.golang.org/grpc v1.72.0 // indirect - google.golang.org/protobuf v1.36.6 // indirect + go.uber.org/zap v1.27.1 // indirect + golang.org/x/crypto v0.46.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/text v0.32.0 // indirect + google.golang.org/grpc v1.77.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/go.sum b/go.sum index 46120bd..cb43302 100644 --- a/go.sum +++ b/go.sum @@ -1,63 +1,53 @@ c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= -filippo.io/age v1.2.0 h1:vRDp7pUMaAJzXNIWJVAZnEf/Dyi4Vu4wI8S1LBzufhE= -filippo.io/age v1.2.0/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004= filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o= filippo.io/age v1.2.1/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/ardanlabs/darwin/v2 v2.0.0 h1:XCisQMgQ5EG+ZvSEcADEo+pyfIMKyWAGnn5o2TgriYE= github.com/ardanlabs/darwin/v2 v2.0.0/go.mod h1:MubZ2e9DAYGaym0mClSOi183NYahrrfKxvSy1HMhoes= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= -github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE= +github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= -github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= -github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= -github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= -github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80= +github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/drand/drand/v2 v2.0.4 h1:aHV+WAisUaIz/m/xyxRtKGz1WJ5K2Ta1L0csA/ICDRU= -github.com/drand/drand/v2 v2.0.4/go.mod h1:nWBj4w7TA3R8xCoyLzkmsESjTlg4QgNSFAiRR9qZXt8= -github.com/drand/drand/v2 v2.1.2 h1:Q5Fo21BB5PbWQFK0nEB7ldfWlQGxC0qJmvdxjhHGrMY= -github.com/drand/drand/v2 v2.1.2/go.mod h1:8TT+5oKwd+A3dJCFjE/qE0PC8VaF6xcgMpU3TxbRybg= -github.com/drand/go-clients v0.2.1 h1:A2MpNKKrkRue4W/RIWoERjWwImvSpX0im+CqES7oF/c= -github.com/drand/go-clients v0.2.1/go.mod h1:4m2qC/O8lx2Aj6DEIrEZ4kUzAUV6BIjmiSouW6lpYfI= +github.com/drand/drand/v2 v2.1.4 h1:E6f8CX2GMVFwDLm0jQZPJ48/I2/SDYYi7VcCbmMS+2k= +github.com/drand/drand/v2 v2.1.4/go.mod h1:10iN1e7M+MzDzNJQ8Nn7ptMiKK5P9jovz9hpxGGEga8= github.com/drand/go-clients v0.2.3 h1:VEIkoW/S4bOkjrwTUjHlRKnlT08WlobnMVI+0NlYVww= github.com/drand/go-clients v0.2.3/go.mod h1:4Vo4r+ASfbxfYfTKhT0BMkhqaJxYoWbTJjo8u7Znqkw= -github.com/drand/kyber v1.3.1 h1:E0p6M3II+loMVwTlAp5zu4+GGZFNiRfq02qZxzw2T+Y= -github.com/drand/kyber v1.3.1/go.mod h1:f+mNHjiGT++CuueBrpeMhFNdKZAsy0tu03bKq9D5LPA= -github.com/drand/kyber-bls12381 v0.3.1 h1:KWb8l/zYTP5yrvKTgvhOrk2eNPscbMiUOIeWBnmUxGo= -github.com/drand/kyber-bls12381 v0.3.1/go.mod h1:H4y9bLPu7KZA/1efDg+jtJ7emKx+ro3PU7/jWUVt140= -github.com/drand/kyber-bls12381 v0.3.3 h1:sLl0ILJtB4+POHAKq6tdnWyg+iXADE0LjVKN91RI8JI= -github.com/drand/kyber-bls12381 v0.3.3/go.mod h1:uVRWtcZDAApOWFMwoJVcTfC4csVxXmpkdoSCUZJ5QOY= +github.com/drand/kyber v1.3.2 h1:Cf3NNcb5bV3eODopr3XVHzImjDK40GiObhFUFG93Zeo= +github.com/drand/kyber v1.3.2/go.mod h1:ciDFWoC7ajb89niGJnS4C1Xeo4lSJMmbi+km5w8juAI= +github.com/drand/kyber-bls12381 v0.3.4 h1:rrmYcRcXmtOAvKWVBxRQxi22qNMVcS2Jz7MAebZQJxI= +github.com/drand/kyber-bls12381 v0.3.4/go.mod h1:jh3IGIAQfdLrdNKYz1HWZ3YdfJM0DWlN1TxXkh60utk= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= +github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -67,14 +57,12 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kilic/bls12-381 v0.1.0 h1:encrdjqKMEvabVQ7qYOKu1OvhqpK4s47wDYtNiPtlp4= github.com/kilic/bls12-381 v0.1.0/go.mod h1:vDTTHJONJ6G+P2R74EhnyotQDTliQDnFEwhdmfzw1ig= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -85,8 +73,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= -github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nikkolasg/hexjson v0.1.0 h1:Cgi1MSZVQFoJKYeRpBNEcdF3LB+Zo4fYKsDz7h8uJYQ= @@ -95,93 +81,75 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= -github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= -github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc= +github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= go.dedis.ch/protobuf v1.0.11/go.mod h1:97QR256dnkimeNdfmURz0wAMNVbd1VmLXhG1CrTYrJ4= -go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= -go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= -go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 h1:wKguEg1hsxI2/L3hUYrpo1RVi48K+uTyzKqprwLXsb8= -google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142/go.mod h1:d6be+8HhtEtucleCbxpPW9PA9XwISACu8nvpPqF0BVo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2 h1:IqsN8hx+lWLqlN+Sc3DoMy/watjofWiU8sRFgQ8fhKM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= -google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= -google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20251213004720-97cd9d5aeac2 h1:7LRqPCEdE4TP4/9psdaB7F2nhZFfBiGJomA5sojLWdU= +google.golang.org/genproto/googleapis/api v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= -rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/networks/fixed/fixed.go b/networks/fixed/fixed.go index c6f34be..960297f 100644 --- a/networks/fixed/fixed.go +++ b/networks/fixed/fixed.go @@ -3,11 +3,12 @@ package fixed import ( "encoding/json" - "errors" "time" chain "github.com/drand/drand/v2/common" "github.com/drand/drand/v2/crypto" + "github.com/drand/tlock/networks" + "github.com/drand/tlock/networks/http" "github.com/drand/kyber" ) @@ -22,9 +23,8 @@ type Network struct { fixedSig []byte } -// ErrNotUnchained represents an error when the informed chain belongs to a -// chained network. -var ErrNotUnchained = errors.New("not an unchained network") +// Check Network implements the networks.Network interface +var _ networks.Network = &Network{} // NewNetwork constructs a network with static, fixed data func NewNetwork(chainHash string, publicKey kyber.Point, sch *crypto.Scheme, period time.Duration, genesis int64, sig []byte) (*Network, error) { @@ -34,7 +34,7 @@ func NewNetwork(chainHash string, publicKey kyber.Point, sch *crypto.Scheme, per case crypto.UnchainedSchemeID: case crypto.BN254UnchainedOnG1SchemeID: default: - return nil, ErrNotUnchained + return nil, http.ErrNotUnchained } return &Network{ @@ -74,7 +74,8 @@ func FromInfo(jsonInfo string) (*Network, error) { } func (n *Network) SetSignature(sig []byte) { - n.fixedSig = sig + n.fixedSig = make([]byte, len(sig)) + copy(n.fixedSig, sig) } // ChainHash returns the chain hash for this network. @@ -82,11 +83,6 @@ func (n *Network) ChainHash() string { return n.chainHash } -// Current returns the current round for that network at the given date. -func (n *Network) Current(date time.Time) uint64 { - return chain.CurrentRound(date.Unix(), n.period, n.genesis) -} - // PublicKey returns the kyber point needed for encryption and decryption. func (n *Network) PublicKey() kyber.Point { return n.publicKey diff --git a/networks/fixed/fixed_test.go b/networks/fixed/fixed_test.go index 6782a60..f14f9d4 100644 --- a/networks/fixed/fixed_test.go +++ b/networks/fixed/fixed_test.go @@ -1,12 +1,11 @@ package fixed import ( - "reflect" "testing" "time" - "github.com/drand/drand/v2/crypto" - "github.com/drand/kyber" + "github.com/drand/tlock/networks/http" + "github.com/stretchr/testify/require" ) @@ -23,7 +22,7 @@ func TestFromInfo(t *testing.T) { jsonStr: `{"public_key":"868f005eb8e6e4ca0a47c8a77ceaa5309a47978a7c71bc5cce96366b5d7a569937c529eeda66c7293784a9402801af31","period":30,"genesis_time":1595431050,"genesis_seed":"176f93498eac9ca337150b46d21dd58673ea4e3581185f869672e59fa4cb390a","chain_hash":"8990e7a9aaed2ffed73dbd7092123d6f289930540d7651336225dc172e51b2ce","scheme":"pedersen-bls-chained","beacon_id":"default"}`, wantHash: "", wantScheme: "", - wantErr: ErrNotUnchained, + wantErr: http.ErrNotUnchained, }, { name: "evmnet", jsonStr: `{"public_key":"07e1d1d335df83fa98462005690372c643340060d205306a9aa8106b6bd0b3820557ec32c2ad488e4d4f6008f89a346f18492092ccc0d594610de2732c8b808f0095685ae3a85ba243747b1b2f426049010f6b73a0cf1d389351d5aaaa1047f6297d3a4f9749b33eb2d904c9d9ebf17224150ddd7abd7567a9bec6c74480ee0b","period":3,"genesis_time":1727521075,"genesis_seed":"cd7ad2f0e0cce5d8c288f2dd016ffe7bc8dc88dbb229b3da2b6ad736490dfed6","chain_hash":"04f1e9062b8a81f848fded9c12306733282b2727ecced50032187751166ec8c3","scheme":"bls-bn254-unchained-on-g1","beacon_id":"evmnet"}`, @@ -44,330 +43,20 @@ func TestFromInfo(t *testing.T) { require.ErrorIs(t, err, tt.wantErr) if err == nil { if got.ChainHash() != tt.wantHash { - t.Errorf("FromInfo() got = %v, want %v", got.ChainHash(), tt.wantHash) + t.Errorf("ChainHash() got = %v, want %v", got.ChainHash(), tt.wantHash) } if got.Scheme().Name != tt.wantScheme { - t.Errorf("FromInfo() got = %v, want %v", got.ChainHash(), tt.wantHash) + t.Errorf("Scheme() got = %v, want %v", got.Scheme().Name, tt.wantScheme) } require.Equal(t, uint64(1), got.RoundNumber(time.Unix(got.genesis, 0))) - } - }) - } -} -func TestNetwork_ChainHash(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - tests := []struct { - name string - fields fields - want string - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - if got := n.ChainHash(); got != tt.want { - t.Errorf("ChainHash() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNetwork_Current(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - type args struct { - date time.Time - } - tests := []struct { - name string - fields fields - args args - want uint64 - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - if got := n.Current(tt.args.date); got != tt.want { - t.Errorf("Current() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNetwork_PublicKey(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - tests := []struct { - name string - fields fields - want kyber.Point - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - if got := n.PublicKey(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("PublicKey() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNetwork_RoundNumber(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - type args struct { - t time.Time - } - tests := []struct { - name string - fields fields - args args - want uint64 - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - if got := n.RoundNumber(tt.args.t); got != tt.want { - t.Errorf("RoundNumber() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNetwork_Scheme(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - tests := []struct { - name string - fields fields - want crypto.Scheme - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - if got := n.Scheme(); !reflect.DeepEqual(got, tt.want) { - t.Errorf("Scheme() = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNetwork_SetSignature(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - type args struct { - sig []byte - } - tests := []struct { - name string - fields fields - args args - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - n.SetSignature(tt.args.sig) - }) - } -} - -func TestNetwork_Signature(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - type args struct { - in0 uint64 - } - tests := []struct { - name string - fields fields - args args - want []byte - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - got, err := n.Signature(tt.args.in0) - if (err != nil) != tt.wantErr { - t.Errorf("Signature() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Signature() got = %v, want %v", got, tt.want) - } - }) - } -} - -func TestNetwork_SwitchChainHash(t *testing.T) { - type fields struct { - chainHash string - publicKey kyber.Point - scheme *crypto.Scheme - period time.Duration - genesis int64 - fixedSig []byte - } - type args struct { - c string - } - tests := []struct { - name string - fields fields - args args - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - n := &Network{ - chainHash: tt.fields.chainHash, - publicKey: tt.fields.publicKey, - scheme: tt.fields.scheme, - period: tt.fields.period, - genesis: tt.fields.genesis, - fixedSig: tt.fields.fixedSig, - } - if err := n.SwitchChainHash(tt.args.c); (err != nil) != tt.wantErr { - t.Errorf("SwitchChainHash() error = %v, wantErr %v", err, tt.wantErr) - } - }) - } -} - -func TestNewNetwork(t *testing.T) { - type args struct { - chainHash string - publicKey kyber.Point - sch *crypto.Scheme - period time.Duration - genesis int64 - sig []byte - } - tests := []struct { - name string - args args - want *Network - wantErr bool - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, err := NewNetwork(tt.args.chainHash, tt.args.publicKey, tt.args.sch, tt.args.period, tt.args.genesis, tt.args.sig) - if (err != nil) != tt.wantErr { - t.Errorf("NewNetwork() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewNetwork() got = %v, want %v", got, tt.want) + sig := []byte{42, 41, 42, 41, 43, 43} + got.SetSignature(sig) + sig2, err := got.Signature(42) + require.NoError(t, err) + require.Equal(t, sig, sig2) + require.NoError(t, got.SwitchChainHash("testing")) + require.Equal(t, "testing", got.ChainHash()) } }) } diff --git a/networks/http/http.go b/networks/http/http.go index e391f72..f4104a0 100644 --- a/networks/http/http.go +++ b/networks/http/http.go @@ -2,19 +2,23 @@ package http import ( + "bytes" "context" "encoding/hex" + "encoding/json" "errors" "fmt" "log" + "log/slog" "net" "net/http" "net/url" "strings" "time" - chain "github.com/drand/drand/v2/common" + "github.com/drand/drand/v2/common/chain" "github.com/drand/drand/v2/crypto" + "github.com/drand/tlock/networks" dhttp "github.com/drand/go-clients/client/http" dclient "github.com/drand/go-clients/drand" @@ -22,14 +26,12 @@ import ( ) // timeout represents the maximum amount of time to wait for network operations. -const timeout = 5 * time.Second +const timeout = 15 * time.Second // ErrNotUnchained represents an error when the informed chain belongs to a // chained network. var ErrNotUnchained = errors.New("not an unchained network") -// ============================================================================= - // Network represents the network support using the drand http client. type Network struct { chainHash string @@ -41,6 +43,42 @@ type Network struct { genesis int64 } +// Check Network implements the networks.Network interface +var _ networks.Network = &Network{} + +func NewFromJSON(jsonStr string) (*Network, error) { + info := new(chain.Info) + err := json.Unmarshal([]byte(jsonStr), &info) + if err != nil { + slog.Warn("Unable to parse chain info as json, trying as Protobuf", "error", err) + info, err = chain.InfoFromJSON(bytes.NewBufferString(jsonStr)) + if err != nil { + return nil, fmt.Errorf("unmarshal json error: %w on %q", err, jsonStr) + } + } + client, err := dhttp.NewWithInfo(nil, "", info, transport()) + if err != nil { + return nil, fmt.Errorf("creating client: %w", err) + } + + sch, err := crypto.SchemeFromName(info.Scheme) + if err != nil { + return nil, ErrNotUnchained + } + network := Network{ + chainHash: info.HashString(), + host: "", + client: client, + publicKey: info.PublicKey, + scheme: *sch, + period: info.Period, + genesis: info.GenesisTime, + } + + return &network, nil + +} + // NewNetwork constructs a network for use that will use the http client. func NewNetwork(host string, chainHash string) (*Network, error) { if !strings.HasPrefix(host, "http") { @@ -78,7 +116,12 @@ func NewNetwork(host string, chainHash string) (*Network, error) { return nil, ErrNotUnchained } - if sch.Name == crypto.DefaultSchemeID { + switch sch.Name { + case crypto.ShortSigSchemeID: + case crypto.SigsOnG1ID: + case crypto.UnchainedSchemeID: + case crypto.BN254UnchainedOnG1SchemeID: + default: return nil, ErrNotUnchained } @@ -100,11 +143,6 @@ func (n *Network) ChainHash() string { return n.chainHash } -// Current returns the current round for that network at the given date. -func (n *Network) Current(date time.Time) uint64 { - return chain.CurrentRound(date.Unix(), n.period, n.genesis) -} - // PublicKey returns the kyber point needed for encryption and decryption. func (n *Network) PublicKey() kyber.Point { return n.publicKey @@ -136,7 +174,7 @@ func (n *Network) RoundNumber(t time.Time) uint64 { return n.client.RoundAt(t) } -// SwitchChainHash allows to start using another chainhash on the same host network +// SwitchChainHash allows to start using another chainHash on the same host network func (n *Network) SwitchChainHash(new string) error { test, err := NewNetwork(n.host, new) if err != nil { @@ -146,8 +184,6 @@ func (n *Network) SwitchChainHash(new string) error { return nil } -// ============================================================================= - // transport sets reasonable defaults for the connection. func transport() *http.Transport { return &http.Transport{ diff --git a/networks/network.go b/networks/network.go new file mode 100644 index 0000000..52c143d --- /dev/null +++ b/networks/network.go @@ -0,0 +1,20 @@ +// Package networks allows to represent and work with various tlock-compatible networks. +package networks + +import ( + "time" + + "github.com/drand/drand/v2/crypto" + "github.com/drand/kyber" +) + +// Network represents a system that provides support for encrypting/decrypting +// a DEK based on a future time. +type Network interface { + ChainHash() string + PublicKey() kyber.Point + Scheme() crypto.Scheme + Signature(roundNumber uint64) ([]byte, error) + SwitchChainHash(string) error + RoundNumber(time.Time) uint64 +} diff --git a/testdata/decryptedFile.bin b/testdata/decryptedFile.bin index 67530ac..9aad764 100644 --- a/testdata/decryptedFile.bin +++ b/testdata/decryptedFile.bin @@ -4,13 +4,13 @@ SHELL := /bin/bash # Local support run-encrypt: - go run app/tle/main.go -n="http://pl-us.testnet.drand.sh/" -c="7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf" -D=30s -o=encryptedFile makefile + go run app/tle/main.go -n="http://pl-us.testnet.drand.sh/" -c="cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5" -D=30s -o=encryptedFile makefile run-decrypt: go run app/tle/main.go -d -n="http://pl-us.testnet.drand.sh/" -o=decryptedFile encryptedFile run-encrypt-a: - go run app/tle/main.go -a -n="http://pl-us.testnet.drand.sh/" -c="7672797f548f3f4748ac4bf3352fc6c6b6468c9ad40ad456a397545c6e2df5bf" -D=30s -o=encryptedArmor.pem makefile + go run app/tle/main.go -a -n="http://pl-us.testnet.drand.sh/" -c="cc9c398442737cbd141526600919edd69f1d6f9b4adb67e4d912fbc64341a9a5" -D=30s -o=encryptedArmor.pem makefile run-decrypt-a: go run app/tle/main.go -d -n="http://pl-us.testnet.drand.sh/" -o=decryptedArmor.pem encryptedArmor.pem @@ -26,4 +26,4 @@ tidy: deps-upgrade: go get -u -v ./... go mod tidy - go mod vendor \ No newline at end of file + go mod vendor diff --git a/tlock.go b/tlock.go index 569e2f4..c7032ad 100644 --- a/tlock.go +++ b/tlock.go @@ -10,6 +10,8 @@ import ( "io" "time" + "github.com/drand/tlock/networks" + "filippo.io/age" "filippo.io/age/armor" chain "github.com/drand/drand/v2/common" @@ -25,24 +27,9 @@ import ( var ErrTooEarly = errors.New("too early to decrypt") var ErrInvalidPublicKey = errors.New("the public key received from the network to encrypt this was infinity and thus insecure") -// ============================================================================= - -// Network represents a system that provides support for encrypting/decrypting -// a DEK based on a future time. -type Network interface { - ChainHash() string - Current(time.Time) uint64 - PublicKey() kyber.Point - Scheme() crypto.Scheme - Signature(roundNumber uint64) ([]byte, error) - SwitchChainHash(string) error -} - -// ============================================================================= - // Tlock provides an API for timelock encryption and decryption. type Tlock struct { - network Network + network networks.Network trustChainhash bool } @@ -50,7 +37,7 @@ type Tlock struct { // can be decrypted until the future. By default a new network will trust the // chainhash it sees in ciphertexts and try and use these unless Strict was // called to prevent it. -func New(network Network) Tlock { +func New(network networks.Network) Tlock { return Tlock{ network: network, trustChainhash: true, @@ -118,7 +105,7 @@ func (t Tlock) Metadata(dst io.Writer) (err error) { scheme := t.network.Scheme() metadata := Metadata{ ChainHash: t.network.ChainHash(), - Current: t.network.Current(time.Now()), + Current: t.network.RoundNumber(time.Now()), PublicKey: t.network.PublicKey().String(), Scheme: scheme.String(), } diff --git a/tlock_age.go b/tlock_age.go index 0a57d1c..67f1d04 100644 --- a/tlock_age.go +++ b/tlock_age.go @@ -11,6 +11,7 @@ import ( "filippo.io/age" chain "github.com/drand/drand/v2/common" + "github.com/drand/tlock/networks" ) var ErrWrongChainhash = errors.New("invalid chainhash") @@ -18,18 +19,18 @@ var ErrWrongChainhash = errors.New("invalid chainhash") // Recipient implements the age Recipient interface. This is used to encrypt // data with the age Encrypt API. type Recipient struct { - network Network + network networks.Network roundNumber uint64 } -func NewRecipient(network Network, roundNumber uint64) *Recipient { +func NewRecipient(network networks.Network, roundNumber uint64) *Recipient { return &Recipient{ network: network, roundNumber: roundNumber, } } -func (t *Recipient) SetNetwork(network Network) { +func (t *Recipient) SetNetwork(network networks.Network) { t.network = network } @@ -83,18 +84,18 @@ func (t *Recipient) String() string { // Identity implements the age Identity interface. This is used to decrypt // data with the age Decrypt API. type Identity struct { - network Network + network networks.Network trustChainhash bool } -func NewIdentity(network Network, trustChainhash bool) *Identity { +func NewIdentity(network networks.Network, trustChainhash bool) *Identity { return &Identity{ network: network, trustChainhash: trustChainhash, } } -func (t *Identity) SetNetwork(network Network) { +func (t *Identity) SetNetwork(network networks.Network) { t.network = network } @@ -150,7 +151,7 @@ func (t *Identity) Unwrap(stanzas []*age.Stanza) ([]byte, error) { "%w: expected round %d > %d current round", ErrTooEarly, roundNumber, - t.network.Current(time.Now())) + t.network.RoundNumber(time.Now())) } beacon := chain.Beacon{ diff --git a/tlock_test.go b/tlock_test.go index cc973c0..d47edda 100644 --- a/tlock_test.go +++ b/tlock_test.go @@ -44,7 +44,6 @@ func TestEarlyDecryptionWithDuration(t *testing.T) { network, err := http.NewNetwork(host, hash) require.NoError(t, err) - // ========================================================================= // Encrypt // Read the plaintext data to be encrypted. @@ -62,7 +61,6 @@ func TestEarlyDecryptionWithDuration(t *testing.T) { err = tlock.New(network).Encrypt(&cipherData, in, roundNumber) require.NoError(t, err) - // ========================================================================= // Decrypt // Write the decoded information to this buffer. @@ -79,7 +77,6 @@ func TestEarlyDecryptionWithRound(t *testing.T) { network, err := http.NewNetwork(testnetHost, testnetUnchainedOnEVM) require.NoError(t, err) - // ========================================================================= // Encrypt // Read the plaintext data to be encrypted. @@ -93,7 +90,6 @@ func TestEarlyDecryptionWithRound(t *testing.T) { err = tlock.New(network).Encrypt(&cipherData, in, futureRound) require.NoError(t, err) - // ========================================================================= // Decrypt // Write the decoded information to this buffer. @@ -112,7 +108,6 @@ func TestEncryptionWithDuration(t *testing.T) { network, err := http.NewNetwork(testnetHost, testnetUnchainedOnEVM) require.NoError(t, err) - // ========================================================================= // Encrypt // Read the plaintext data to be encrypted. @@ -130,7 +125,6 @@ func TestEncryptionWithDuration(t *testing.T) { err = tlock.New(network).Encrypt(&cipherData, in, roundNumber) require.NoError(t, err) - // ========================================================================= // Decrypt time.Sleep(5 * time.Second) @@ -215,9 +209,7 @@ func TestEncryptionWithRound(t *testing.T) { network, err := http.NewNetwork(testnetHost, testnetUnchainedOnEVM) require.NoError(t, err) - // ========================================================================= // Encrypt - // Read the plaintext data to be encrypted. in, err := os.Open("testdata/data.txt") require.NoError(t, err) @@ -230,9 +222,7 @@ func TestEncryptionWithRound(t *testing.T) { err = tlock.New(network).Encrypt(&cipherData, in, futureRound) require.NoError(t, err) - // ========================================================================= // Decrypt - var plainData bytes.Buffer // Wait for the future beacon to exist.