mirror of
https://github.com/42wim/matterbridge.git
synced 2024-11-24 21:19:32 +01:00
Add dependencies/vendor (whatsapp)
This commit is contained in:
parent
e7b193788a
commit
e3cafeaf92
26
go.mod
26
go.mod
@ -2,10 +2,8 @@ module github.com/42wim/matterbridge
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
|
github.com/42wim/go-gitter v0.0.0-20170828205020-017310c2d557
|
||||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
|
|
||||||
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
|
github.com/Benau/tgsconverter v0.0.0-20210809170556-99f4a4f6337f
|
||||||
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
|
github.com/Philipp15b/go-steam v1.0.1-0.20200727090957-6ae9b3c0a560
|
||||||
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c
|
|
||||||
github.com/SevereCloud/vksdk/v2 v2.13.1
|
github.com/SevereCloud/vksdk/v2 v2.13.1
|
||||||
github.com/bwmarrin/discordgo v0.24.0
|
github.com/bwmarrin/discordgo v0.24.0
|
||||||
github.com/d5/tengo/v2 v2.10.1
|
github.com/d5/tengo/v2 v2.10.1
|
||||||
@ -32,6 +30,7 @@ require (
|
|||||||
github.com/mattermost/mattermost-server/v5 v5.39.3
|
github.com/mattermost/mattermost-server/v5 v5.39.3
|
||||||
github.com/mattermost/mattermost-server/v6 v6.4.2
|
github.com/mattermost/mattermost-server/v6 v6.4.2
|
||||||
github.com/mattn/godown v0.0.1
|
github.com/mattn/godown v0.0.1
|
||||||
|
github.com/mdp/qrterminal v1.0.1
|
||||||
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
|
github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9
|
||||||
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
|
github.com/paulrosania/go-charset v0.0.0-20190326053356-55c9d7a5834c
|
||||||
github.com/rs/xid v1.3.0
|
github.com/rs/xid v1.3.0
|
||||||
@ -46,17 +45,22 @@ require (
|
|||||||
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
github.com/writeas/go-strip-markdown v2.0.1+incompatible
|
||||||
github.com/yaegashi/msgraph.go v0.1.4
|
github.com/yaegashi/msgraph.go v0.1.4
|
||||||
github.com/zfjagann/golang-ring v0.0.0-20210116075443-7c86fdb43134
|
github.com/zfjagann/golang-ring v0.0.0-20210116075443-7c86fdb43134
|
||||||
|
go.mau.fi/whatsmeow v0.0.0-20220128124639-e64fb976bf15
|
||||||
golang.org/x/image v0.0.0-20220302094943-723b81ca9867
|
golang.org/x/image v0.0.0-20220302094943-723b81ca9867
|
||||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
|
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gomod.garykim.dev/nc-talk v0.3.0
|
gomod.garykim.dev/nc-talk v0.3.0
|
||||||
|
google.golang.org/protobuf v1.27.1
|
||||||
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
|
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
|
||||||
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
|
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
|
||||||
|
modernc.org/sqlite v1.14.5
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
|
||||||
github.com/Benau/go_rlottie v0.0.0-20210807002906-98c1b2421989 // indirect
|
github.com/Benau/go_rlottie v0.0.0-20210807002906-98c1b2421989 // indirect
|
||||||
github.com/Jeffail/gabs v1.4.0 // indirect
|
github.com/Jeffail/gabs v1.4.0 // indirect
|
||||||
|
github.com/Rhymen/go-whatsapp v0.1.2-0.20211102134409-31a2e740845c // indirect
|
||||||
github.com/apex/log v1.9.0 // indirect
|
github.com/apex/log v1.9.0 // indirect
|
||||||
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
|
github.com/av-elier/go-decimal-to-rational v0.0.0-20191127152832-89e6aad02ecf // indirect
|
||||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||||
@ -72,6 +76,7 @@ require (
|
|||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||||
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
github.com/kettek/apng v0.0.0-20191108220231-414630eed80f // indirect
|
||||||
github.com/klauspost/compress v1.14.2 // indirect
|
github.com/klauspost/compress v1.14.2 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||||
@ -100,12 +105,12 @@ require (
|
|||||||
github.com/philhofer/fwd v1.1.1 // indirect
|
github.com/philhofer/fwd v1.1.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||||
github.com/rickb777/date v1.12.4 // indirect
|
github.com/rickb777/date v1.12.4 // indirect
|
||||||
github.com/rickb777/plural v1.2.0 // indirect
|
github.com/rickb777/plural v1.2.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
|
github.com/shazow/rateio v0.0.0-20200113175441-4461efc8bdc4 // indirect
|
||||||
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
|
github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 // indirect
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
|
||||||
github.com/spf13/afero v1.6.0 // indirect
|
github.com/spf13/afero v1.6.0 // indirect
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
@ -119,20 +124,33 @@ require (
|
|||||||
github.com/wiggin77/cfg v1.0.2 // indirect
|
github.com/wiggin77/cfg v1.0.2 // indirect
|
||||||
github.com/wiggin77/merror v1.0.3 // indirect
|
github.com/wiggin77/merror v1.0.3 // indirect
|
||||||
github.com/wiggin77/srslog v1.0.1 // indirect
|
github.com/wiggin77/srslog v1.0.1 // indirect
|
||||||
|
go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910 // indirect
|
||||||
go.uber.org/atomic v1.9.0 // indirect
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
go.uber.org/multierr v1.7.0 // indirect
|
go.uber.org/multierr v1.7.0 // indirect
|
||||||
go.uber.org/zap v1.17.0 // indirect
|
go.uber.org/zap v1.17.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
|
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 // indirect
|
||||||
|
golang.org/x/mod v0.5.1 // indirect
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||||
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
|
||||||
|
golang.org/x/tools v0.1.7 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // indirect
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
lukechampine.com/uint128 v1.1.1 // indirect
|
||||||
|
modernc.org/cc/v3 v3.35.22 // indirect
|
||||||
|
modernc.org/ccgo/v3 v3.15.1 // indirect
|
||||||
|
modernc.org/libc v1.14.1 // indirect
|
||||||
|
modernc.org/mathutil v1.4.1 // indirect
|
||||||
|
modernc.org/memory v1.0.5 // indirect
|
||||||
|
modernc.org/opt v0.1.1 // indirect
|
||||||
|
modernc.org/strutil v1.1.1 // indirect
|
||||||
|
modernc.org/token v1.0.0 // indirect
|
||||||
|
rsc.io/qr v0.2.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
//replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
|
//replace github.com/matrix-org/gomatrix => github.com/matterbridge/gomatrix v0.0.0-20220205235239-607eb9ee6419
|
||||||
|
27
vendor/filippo.io/edwards25519/LICENSE
generated
vendored
Normal file
27
vendor/filippo.io/edwards25519/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
14
vendor/filippo.io/edwards25519/README.md
generated
vendored
Normal file
14
vendor/filippo.io/edwards25519/README.md
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# filippo.io/edwards25519
|
||||||
|
|
||||||
|
```
|
||||||
|
import "filippo.io/edwards25519"
|
||||||
|
```
|
||||||
|
|
||||||
|
This library implements the edwards25519 elliptic curve, exposing the necessary APIs to build a wide array of higher-level primitives.
|
||||||
|
Read the docs at [pkg.go.dev/filippo.io/edwards25519](https://pkg.go.dev/filippo.io/edwards25519).
|
||||||
|
|
||||||
|
The code is originally derived from Adam Langley's internal implementation in the Go standard library, and includes George Tankersley's [performance improvements](https://golang.org/cl/71950). It was then further developed by Henry de Valence for use in ristretto255.
|
||||||
|
|
||||||
|
Most users don't need this package, and should instead use `crypto/ed25519` for signatures, `golang.org/x/crypto/curve25519` for Diffie-Hellman, or `github.com/gtank/ristretto255` for prime order group logic. However, for anyone currently using a fork of `crypto/ed25519/internal/edwards25519` or `github.com/agl/edwards25519`, this package should be a safer, faster, and more powerful alternative.
|
||||||
|
|
||||||
|
Since this package is meant to curb proliferation of edwards25519 implementations in the Go ecosystem, it welcomes requests for new APIs or reviewable performance improvements.
|
20
vendor/filippo.io/edwards25519/doc.go
generated
vendored
Normal file
20
vendor/filippo.io/edwards25519/doc.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package edwards25519 implements group logic for the twisted Edwards curve
|
||||||
|
//
|
||||||
|
// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
|
||||||
|
//
|
||||||
|
// This is better known as the Edwards curve equivalent to Curve25519, and is
|
||||||
|
// the curve used by the Ed25519 signature scheme.
|
||||||
|
//
|
||||||
|
// Most users don't need this package, and should instead use crypto/ed25519 for
|
||||||
|
// signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or
|
||||||
|
// github.com/gtank/ristretto255 for prime order group logic.
|
||||||
|
//
|
||||||
|
// However, developers who do need to interact with low-level edwards25519
|
||||||
|
// operations can use this package, which is an extended version of
|
||||||
|
// crypto/ed25519/internal/edwards25519 from the standard library repackaged as
|
||||||
|
// an importable module.
|
||||||
|
package edwards25519
|
428
vendor/filippo.io/edwards25519/edwards25519.go
generated
vendored
Normal file
428
vendor/filippo.io/edwards25519/edwards25519.go
generated
vendored
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"filippo.io/edwards25519/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Point types.
|
||||||
|
|
||||||
|
type projP1xP1 struct {
|
||||||
|
X, Y, Z, T field.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
type projP2 struct {
|
||||||
|
X, Y, Z field.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point represents a point on the edwards25519 curve.
|
||||||
|
//
|
||||||
|
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||||
|
// are allowed to alias.
|
||||||
|
//
|
||||||
|
// The zero value is NOT valid, and it may be used only as a receiver.
|
||||||
|
type Point struct {
|
||||||
|
// The point is internally represented in extended coordinates (X, Y, Z, T)
|
||||||
|
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
|
||||||
|
x, y, z, t field.Element
|
||||||
|
|
||||||
|
// Make the type not comparable (i.e. used with == or as a map key), as
|
||||||
|
// equivalent points can be represented by different Go values.
|
||||||
|
_ incomparable
|
||||||
|
}
|
||||||
|
|
||||||
|
type incomparable [0]func()
|
||||||
|
|
||||||
|
func checkInitialized(points ...*Point) {
|
||||||
|
for _, p := range points {
|
||||||
|
if p.x == (field.Element{}) && p.y == (field.Element{}) {
|
||||||
|
panic("edwards25519: use of uninitialized Point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type projCached struct {
|
||||||
|
YplusX, YminusX, Z, T2d field.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
type affineCached struct {
|
||||||
|
YplusX, YminusX, T2d field.Element
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructors.
|
||||||
|
|
||||||
|
func (v *projP2) Zero() *projP2 {
|
||||||
|
v.X.Zero()
|
||||||
|
v.Y.One()
|
||||||
|
v.Z.One()
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// identity is the point at infinity.
|
||||||
|
var identity, _ = new(Point).SetBytes([]byte{
|
||||||
|
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
|
||||||
|
|
||||||
|
// NewIdentityPoint returns a new Point set to the identity.
|
||||||
|
func NewIdentityPoint() *Point {
|
||||||
|
return new(Point).Set(identity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// generator is the canonical curve basepoint. See TestGenerator for the
|
||||||
|
// correspondence of this encoding with the values in RFC 8032.
|
||||||
|
var generator, _ = new(Point).SetBytes([]byte{
|
||||||
|
0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||||
|
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||||
|
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||||
|
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66})
|
||||||
|
|
||||||
|
// NewGeneratorPoint returns a new Point set to the canonical generator.
|
||||||
|
func NewGeneratorPoint() *Point {
|
||||||
|
return new(Point).Set(generator)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *projCached) Zero() *projCached {
|
||||||
|
v.YplusX.One()
|
||||||
|
v.YminusX.One()
|
||||||
|
v.Z.One()
|
||||||
|
v.T2d.Zero()
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *affineCached) Zero() *affineCached {
|
||||||
|
v.YplusX.One()
|
||||||
|
v.YminusX.One()
|
||||||
|
v.T2d.Zero()
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assignments.
|
||||||
|
|
||||||
|
// Set sets v = u, and returns v.
|
||||||
|
func (v *Point) Set(u *Point) *Point {
|
||||||
|
*v = *u
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoding.
|
||||||
|
|
||||||
|
// Bytes returns the canonical 32-byte encoding of v, according to RFC 8032,
|
||||||
|
// Section 5.1.2.
|
||||||
|
func (v *Point) Bytes() []byte {
|
||||||
|
// This function is outlined to make the allocations inline in the caller
|
||||||
|
// rather than happen on the heap.
|
||||||
|
var buf [32]byte
|
||||||
|
return v.bytes(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Point) bytes(buf *[32]byte) []byte {
|
||||||
|
checkInitialized(v)
|
||||||
|
|
||||||
|
var zInv, x, y field.Element
|
||||||
|
zInv.Invert(&v.z) // zInv = 1 / Z
|
||||||
|
x.Multiply(&v.x, &zInv) // x = X / Z
|
||||||
|
y.Multiply(&v.y, &zInv) // y = Y / Z
|
||||||
|
|
||||||
|
out := copyFieldElement(buf, &y)
|
||||||
|
out[31] |= byte(x.IsNegative() << 7)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
var feOne = new(field.Element).One()
|
||||||
|
|
||||||
|
// SetBytes sets v = x, where x is a 32-byte encoding of v. If x does not
|
||||||
|
// represent a valid point on the curve, SetBytes returns nil and an error and
|
||||||
|
// the receiver is unchanged. Otherwise, SetBytes returns v.
|
||||||
|
//
|
||||||
|
// Note that SetBytes accepts all non-canonical encodings of valid points.
|
||||||
|
// That is, it follows decoding rules that match most implementations in
|
||||||
|
// the ecosystem rather than RFC 8032.
|
||||||
|
func (v *Point) SetBytes(x []byte) (*Point, error) {
|
||||||
|
// Specifically, the non-canonical encodings that are accepted are
|
||||||
|
// 1) the ones where the field element is not reduced (see the
|
||||||
|
// (*field.Element).SetBytes docs) and
|
||||||
|
// 2) the ones where the x-coordinate is zero and the sign bit is set.
|
||||||
|
//
|
||||||
|
// This is consistent with crypto/ed25519/internal/edwards25519. Read more
|
||||||
|
// at https://hdevalence.ca/blog/2020-10-04-its-25519am, specifically the
|
||||||
|
// "Canonical A, R" section.
|
||||||
|
|
||||||
|
y, err := new(field.Element).SetBytes(x)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("edwards25519: invalid point encoding length")
|
||||||
|
}
|
||||||
|
|
||||||
|
// -x² + y² = 1 + dx²y²
|
||||||
|
// x² + dx²y² = x²(dy² + 1) = y² - 1
|
||||||
|
// x² = (y² - 1) / (dy² + 1)
|
||||||
|
|
||||||
|
// u = y² - 1
|
||||||
|
y2 := new(field.Element).Square(y)
|
||||||
|
u := new(field.Element).Subtract(y2, feOne)
|
||||||
|
|
||||||
|
// v = dy² + 1
|
||||||
|
vv := new(field.Element).Multiply(y2, d)
|
||||||
|
vv = vv.Add(vv, feOne)
|
||||||
|
|
||||||
|
// x = +√(u/v)
|
||||||
|
xx, wasSquare := new(field.Element).SqrtRatio(u, vv)
|
||||||
|
if wasSquare == 0 {
|
||||||
|
return nil, errors.New("edwards25519: invalid point encoding")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the negative square root if the sign bit is set.
|
||||||
|
xxNeg := new(field.Element).Negate(xx)
|
||||||
|
xx = xx.Select(xxNeg, xx, int(x[31]>>7))
|
||||||
|
|
||||||
|
v.x.Set(xx)
|
||||||
|
v.y.Set(y)
|
||||||
|
v.z.One()
|
||||||
|
v.t.Multiply(xx, y) // xy = T / Z
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFieldElement(buf *[32]byte, v *field.Element) []byte {
|
||||||
|
copy(buf[:], v.Bytes())
|
||||||
|
return buf[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conversions.
|
||||||
|
|
||||||
|
func (v *projP2) FromP1xP1(p *projP1xP1) *projP2 {
|
||||||
|
v.X.Multiply(&p.X, &p.T)
|
||||||
|
v.Y.Multiply(&p.Y, &p.Z)
|
||||||
|
v.Z.Multiply(&p.Z, &p.T)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *projP2) FromP3(p *Point) *projP2 {
|
||||||
|
v.X.Set(&p.x)
|
||||||
|
v.Y.Set(&p.y)
|
||||||
|
v.Z.Set(&p.z)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Point) fromP1xP1(p *projP1xP1) *Point {
|
||||||
|
v.x.Multiply(&p.X, &p.T)
|
||||||
|
v.y.Multiply(&p.Y, &p.Z)
|
||||||
|
v.z.Multiply(&p.Z, &p.T)
|
||||||
|
v.t.Multiply(&p.X, &p.Y)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Point) fromP2(p *projP2) *Point {
|
||||||
|
v.x.Multiply(&p.X, &p.Z)
|
||||||
|
v.y.Multiply(&p.Y, &p.Z)
|
||||||
|
v.z.Square(&p.Z)
|
||||||
|
v.t.Multiply(&p.X, &p.Y)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// d is a constant in the curve equation.
|
||||||
|
var d, _ = new(field.Element).SetBytes([]byte{
|
||||||
|
0xa3, 0x78, 0x59, 0x13, 0xca, 0x4d, 0xeb, 0x75,
|
||||||
|
0xab, 0xd8, 0x41, 0x41, 0x4d, 0x0a, 0x70, 0x00,
|
||||||
|
0x98, 0xe8, 0x79, 0x77, 0x79, 0x40, 0xc7, 0x8c,
|
||||||
|
0x73, 0xfe, 0x6f, 0x2b, 0xee, 0x6c, 0x03, 0x52})
|
||||||
|
var d2 = new(field.Element).Add(d, d)
|
||||||
|
|
||||||
|
func (v *projCached) FromP3(p *Point) *projCached {
|
||||||
|
v.YplusX.Add(&p.y, &p.x)
|
||||||
|
v.YminusX.Subtract(&p.y, &p.x)
|
||||||
|
v.Z.Set(&p.z)
|
||||||
|
v.T2d.Multiply(&p.t, d2)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *affineCached) FromP3(p *Point) *affineCached {
|
||||||
|
v.YplusX.Add(&p.y, &p.x)
|
||||||
|
v.YminusX.Subtract(&p.y, &p.x)
|
||||||
|
v.T2d.Multiply(&p.t, d2)
|
||||||
|
|
||||||
|
var invZ field.Element
|
||||||
|
invZ.Invert(&p.z)
|
||||||
|
v.YplusX.Multiply(&v.YplusX, &invZ)
|
||||||
|
v.YminusX.Multiply(&v.YminusX, &invZ)
|
||||||
|
v.T2d.Multiply(&v.T2d, &invZ)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Re)addition and subtraction.
|
||||||
|
|
||||||
|
// Add sets v = p + q, and returns v.
|
||||||
|
func (v *Point) Add(p, q *Point) *Point {
|
||||||
|
checkInitialized(p, q)
|
||||||
|
qCached := new(projCached).FromP3(q)
|
||||||
|
result := new(projP1xP1).Add(p, qCached)
|
||||||
|
return v.fromP1xP1(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract sets v = p - q, and returns v.
|
||||||
|
func (v *Point) Subtract(p, q *Point) *Point {
|
||||||
|
checkInitialized(p, q)
|
||||||
|
qCached := new(projCached).FromP3(q)
|
||||||
|
result := new(projP1xP1).Sub(p, qCached)
|
||||||
|
return v.fromP1xP1(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *projP1xP1) Add(p *Point, q *projCached) *projP1xP1 {
|
||||||
|
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
||||||
|
|
||||||
|
YplusX.Add(&p.y, &p.x)
|
||||||
|
YminusX.Subtract(&p.y, &p.x)
|
||||||
|
|
||||||
|
PP.Multiply(&YplusX, &q.YplusX)
|
||||||
|
MM.Multiply(&YminusX, &q.YminusX)
|
||||||
|
TT2d.Multiply(&p.t, &q.T2d)
|
||||||
|
ZZ2.Multiply(&p.z, &q.Z)
|
||||||
|
|
||||||
|
ZZ2.Add(&ZZ2, &ZZ2)
|
||||||
|
|
||||||
|
v.X.Subtract(&PP, &MM)
|
||||||
|
v.Y.Add(&PP, &MM)
|
||||||
|
v.Z.Add(&ZZ2, &TT2d)
|
||||||
|
v.T.Subtract(&ZZ2, &TT2d)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *projP1xP1) Sub(p *Point, q *projCached) *projP1xP1 {
|
||||||
|
var YplusX, YminusX, PP, MM, TT2d, ZZ2 field.Element
|
||||||
|
|
||||||
|
YplusX.Add(&p.y, &p.x)
|
||||||
|
YminusX.Subtract(&p.y, &p.x)
|
||||||
|
|
||||||
|
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
||||||
|
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
||||||
|
TT2d.Multiply(&p.t, &q.T2d)
|
||||||
|
ZZ2.Multiply(&p.z, &q.Z)
|
||||||
|
|
||||||
|
ZZ2.Add(&ZZ2, &ZZ2)
|
||||||
|
|
||||||
|
v.X.Subtract(&PP, &MM)
|
||||||
|
v.Y.Add(&PP, &MM)
|
||||||
|
v.Z.Subtract(&ZZ2, &TT2d) // flipped sign
|
||||||
|
v.T.Add(&ZZ2, &TT2d) // flipped sign
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *projP1xP1) AddAffine(p *Point, q *affineCached) *projP1xP1 {
|
||||||
|
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
||||||
|
|
||||||
|
YplusX.Add(&p.y, &p.x)
|
||||||
|
YminusX.Subtract(&p.y, &p.x)
|
||||||
|
|
||||||
|
PP.Multiply(&YplusX, &q.YplusX)
|
||||||
|
MM.Multiply(&YminusX, &q.YminusX)
|
||||||
|
TT2d.Multiply(&p.t, &q.T2d)
|
||||||
|
|
||||||
|
Z2.Add(&p.z, &p.z)
|
||||||
|
|
||||||
|
v.X.Subtract(&PP, &MM)
|
||||||
|
v.Y.Add(&PP, &MM)
|
||||||
|
v.Z.Add(&Z2, &TT2d)
|
||||||
|
v.T.Subtract(&Z2, &TT2d)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *projP1xP1) SubAffine(p *Point, q *affineCached) *projP1xP1 {
|
||||||
|
var YplusX, YminusX, PP, MM, TT2d, Z2 field.Element
|
||||||
|
|
||||||
|
YplusX.Add(&p.y, &p.x)
|
||||||
|
YminusX.Subtract(&p.y, &p.x)
|
||||||
|
|
||||||
|
PP.Multiply(&YplusX, &q.YminusX) // flipped sign
|
||||||
|
MM.Multiply(&YminusX, &q.YplusX) // flipped sign
|
||||||
|
TT2d.Multiply(&p.t, &q.T2d)
|
||||||
|
|
||||||
|
Z2.Add(&p.z, &p.z)
|
||||||
|
|
||||||
|
v.X.Subtract(&PP, &MM)
|
||||||
|
v.Y.Add(&PP, &MM)
|
||||||
|
v.Z.Subtract(&Z2, &TT2d) // flipped sign
|
||||||
|
v.T.Add(&Z2, &TT2d) // flipped sign
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doubling.
|
||||||
|
|
||||||
|
func (v *projP1xP1) Double(p *projP2) *projP1xP1 {
|
||||||
|
var XX, YY, ZZ2, XplusYsq field.Element
|
||||||
|
|
||||||
|
XX.Square(&p.X)
|
||||||
|
YY.Square(&p.Y)
|
||||||
|
ZZ2.Square(&p.Z)
|
||||||
|
ZZ2.Add(&ZZ2, &ZZ2)
|
||||||
|
XplusYsq.Add(&p.X, &p.Y)
|
||||||
|
XplusYsq.Square(&XplusYsq)
|
||||||
|
|
||||||
|
v.Y.Add(&YY, &XX)
|
||||||
|
v.Z.Subtract(&YY, &XX)
|
||||||
|
|
||||||
|
v.X.Subtract(&XplusYsq, &v.Y)
|
||||||
|
v.T.Subtract(&ZZ2, &v.Z)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negation.
|
||||||
|
|
||||||
|
// Negate sets v = -p, and returns v.
|
||||||
|
func (v *Point) Negate(p *Point) *Point {
|
||||||
|
checkInitialized(p)
|
||||||
|
v.x.Negate(&p.x)
|
||||||
|
v.y.Set(&p.y)
|
||||||
|
v.z.Set(&p.z)
|
||||||
|
v.t.Negate(&p.t)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns 1 if v is equivalent to u, and 0 otherwise.
|
||||||
|
func (v *Point) Equal(u *Point) int {
|
||||||
|
checkInitialized(v, u)
|
||||||
|
|
||||||
|
var t1, t2, t3, t4 field.Element
|
||||||
|
t1.Multiply(&v.x, &u.z)
|
||||||
|
t2.Multiply(&u.x, &v.z)
|
||||||
|
t3.Multiply(&v.y, &u.z)
|
||||||
|
t4.Multiply(&u.y, &v.z)
|
||||||
|
|
||||||
|
return t1.Equal(&t2) & t3.Equal(&t4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constant-time operations
|
||||||
|
|
||||||
|
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||||
|
func (v *projCached) Select(a, b *projCached, cond int) *projCached {
|
||||||
|
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||||
|
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||||
|
v.Z.Select(&a.Z, &b.Z, cond)
|
||||||
|
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||||
|
func (v *affineCached) Select(a, b *affineCached, cond int) *affineCached {
|
||||||
|
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||||
|
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||||
|
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||||
|
func (v *projCached) CondNeg(cond int) *projCached {
|
||||||
|
v.YplusX.Swap(&v.YminusX, cond)
|
||||||
|
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||||
|
func (v *affineCached) CondNeg(cond int) *affineCached {
|
||||||
|
v.YplusX.Swap(&v.YminusX, cond)
|
||||||
|
v.T2d.Select(new(field.Element).Negate(&v.T2d), &v.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
343
vendor/filippo.io/edwards25519/extra.go
generated
vendored
Normal file
343
vendor/filippo.io/edwards25519/extra.go
generated
vendored
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
// This file contains additional functionality that is not included in the
|
||||||
|
// upstream crypto/ed25519/internal/edwards25519 package.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"filippo.io/edwards25519/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where
|
||||||
|
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||||
|
func (v *Point) ExtendedCoordinates() (X, Y, Z, T *field.Element) {
|
||||||
|
// This function is outlined to make the allocations inline in the caller
|
||||||
|
// rather than happen on the heap. Don't change the style without making
|
||||||
|
// sure it doesn't increase the inliner cost.
|
||||||
|
var e [4]field.Element
|
||||||
|
X, Y, Z, T = v.extendedCoordinates(&e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Point) extendedCoordinates(e *[4]field.Element) (X, Y, Z, T *field.Element) {
|
||||||
|
checkInitialized(v)
|
||||||
|
X = e[0].Set(&v.x)
|
||||||
|
Y = e[1].Set(&v.y)
|
||||||
|
Z = e[2].Set(&v.z)
|
||||||
|
T = e[3].Set(&v.t)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetExtendedCoordinates sets v = (X:Y:Z:T) in extended coordinates where
|
||||||
|
// x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
|
||||||
|
//
|
||||||
|
// If the coordinates are invalid or don't represent a valid point on the curve,
|
||||||
|
// SetExtendedCoordinates returns nil and an error and the receiver is
|
||||||
|
// unchanged. Otherwise, SetExtendedCoordinates returns v.
|
||||||
|
func (v *Point) SetExtendedCoordinates(X, Y, Z, T *field.Element) (*Point, error) {
|
||||||
|
if !isOnCurve(X, Y, Z, T) {
|
||||||
|
return nil, errors.New("edwards25519: invalid point coordinates")
|
||||||
|
}
|
||||||
|
v.x.Set(X)
|
||||||
|
v.y.Set(Y)
|
||||||
|
v.z.Set(Z)
|
||||||
|
v.t.Set(T)
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isOnCurve(X, Y, Z, T *field.Element) bool {
|
||||||
|
var lhs, rhs field.Element
|
||||||
|
XX := new(field.Element).Square(X)
|
||||||
|
YY := new(field.Element).Square(Y)
|
||||||
|
ZZ := new(field.Element).Square(Z)
|
||||||
|
TT := new(field.Element).Square(T)
|
||||||
|
// -x² + y² = 1 + dx²y²
|
||||||
|
// -(X/Z)² + (Y/Z)² = 1 + d(T/Z)²
|
||||||
|
// -X² + Y² = Z² + dT²
|
||||||
|
lhs.Subtract(YY, XX)
|
||||||
|
rhs.Multiply(d, TT).Add(&rhs, ZZ)
|
||||||
|
if lhs.Equal(&rhs) != 1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// xy = T/Z
|
||||||
|
// XY/Z² = T/Z
|
||||||
|
// XY = TZ
|
||||||
|
lhs.Multiply(X, Y)
|
||||||
|
rhs.Multiply(T, Z)
|
||||||
|
return lhs.Equal(&rhs) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesMontgomery converts v to a point on the birationally-equivalent
|
||||||
|
// Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding
|
||||||
|
// according to RFC 7748.
|
||||||
|
//
|
||||||
|
// Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode
|
||||||
|
// to the same value. If v is the identity point, BytesMontgomery returns 32
|
||||||
|
// zero bytes, analogously to the X25519 function.
|
||||||
|
func (v *Point) BytesMontgomery() []byte {
|
||||||
|
// This function is outlined to make the allocations inline in the caller
|
||||||
|
// rather than happen on the heap.
|
||||||
|
var buf [32]byte
|
||||||
|
return v.bytesMontgomery(&buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Point) bytesMontgomery(buf *[32]byte) []byte {
|
||||||
|
checkInitialized(v)
|
||||||
|
|
||||||
|
// RFC 7748, Section 4.1 provides the bilinear map to calculate the
|
||||||
|
// Montgomery u-coordinate
|
||||||
|
//
|
||||||
|
// u = (1 + y) / (1 - y)
|
||||||
|
//
|
||||||
|
// where y = Y / Z.
|
||||||
|
|
||||||
|
var y, recip, u field.Element
|
||||||
|
|
||||||
|
y.Multiply(&v.y, y.Invert(&v.z)) // y = Y / Z
|
||||||
|
recip.Invert(recip.Subtract(feOne, &y)) // r = 1/(1 - y)
|
||||||
|
u.Multiply(u.Add(feOne, &y), &recip) // u = (1 + y)*r
|
||||||
|
|
||||||
|
return copyFieldElement(buf, &u)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultByCofactor sets v = 8 * p, and returns v.
|
||||||
|
func (v *Point) MultByCofactor(p *Point) *Point {
|
||||||
|
checkInitialized(p)
|
||||||
|
result := projP1xP1{}
|
||||||
|
pp := (&projP2{}).FromP3(p)
|
||||||
|
result.Double(pp)
|
||||||
|
pp.FromP1xP1(&result)
|
||||||
|
result.Double(pp)
|
||||||
|
pp.FromP1xP1(&result)
|
||||||
|
result.Double(pp)
|
||||||
|
return v.fromP1xP1(&result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given k > 0, set s = s**(2*i).
|
||||||
|
func (s *Scalar) pow2k(k int) {
|
||||||
|
for i := 0; i < k; i++ {
|
||||||
|
s.Multiply(s, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert sets s to the inverse of a nonzero scalar v, and returns s.
|
||||||
|
//
|
||||||
|
// If t is zero, Invert returns zero.
|
||||||
|
func (s *Scalar) Invert(t *Scalar) *Scalar {
|
||||||
|
// Uses a hardcoded sliding window of width 4.
|
||||||
|
var table [8]Scalar
|
||||||
|
var tt Scalar
|
||||||
|
tt.Multiply(t, t)
|
||||||
|
table[0] = *t
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
table[i+1].Multiply(&table[i], &tt)
|
||||||
|
}
|
||||||
|
// Now table = [t**1, t**3, t**7, t**11, t**13, t**15]
|
||||||
|
// so t**k = t[k/2] for odd k
|
||||||
|
|
||||||
|
// To compute the sliding window digits, use the following Sage script:
|
||||||
|
|
||||||
|
// sage: import itertools
|
||||||
|
// sage: def sliding_window(w,k):
|
||||||
|
// ....: digits = []
|
||||||
|
// ....: while k > 0:
|
||||||
|
// ....: if k % 2 == 1:
|
||||||
|
// ....: kmod = k % (2**w)
|
||||||
|
// ....: digits.append(kmod)
|
||||||
|
// ....: k = k - kmod
|
||||||
|
// ....: else:
|
||||||
|
// ....: digits.append(0)
|
||||||
|
// ....: k = k // 2
|
||||||
|
// ....: return digits
|
||||||
|
|
||||||
|
// Now we can compute s roughly as follows:
|
||||||
|
|
||||||
|
// sage: s = 1
|
||||||
|
// sage: for coeff in reversed(sliding_window(4,l-2)):
|
||||||
|
// ....: s = s*s
|
||||||
|
// ....: if coeff > 0 :
|
||||||
|
// ....: s = s*t**coeff
|
||||||
|
|
||||||
|
// This works on one bit at a time, with many runs of zeros.
|
||||||
|
// The digits can be collapsed into [(count, coeff)] as follows:
|
||||||
|
|
||||||
|
// sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))]
|
||||||
|
|
||||||
|
// Entries of the form (k, 0) turn into pow2k(k)
|
||||||
|
// Entries of the form (1, coeff) turn into a squaring and then a table lookup.
|
||||||
|
// We can fold the squaring into the previous pow2k(k) as pow2k(k+1).
|
||||||
|
|
||||||
|
*s = table[1/2]
|
||||||
|
s.pow2k(127 + 1)
|
||||||
|
s.Multiply(s, &table[1/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[9/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[11/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[13/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[15/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[7/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[15/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[5/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[1/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[15/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[15/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[7/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[3/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[11/2])
|
||||||
|
s.pow2k(5 + 1)
|
||||||
|
s.Multiply(s, &table[11/2])
|
||||||
|
s.pow2k(9 + 1)
|
||||||
|
s.Multiply(s, &table[9/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[3/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[3/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[3/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[9/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[7/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[3/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[13/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[7/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[9/2])
|
||||||
|
s.pow2k(3 + 1)
|
||||||
|
s.Multiply(s, &table[15/2])
|
||||||
|
s.pow2k(4 + 1)
|
||||||
|
s.Multiply(s, &table[11/2])
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// MultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||||
|
//
|
||||||
|
// Execution time depends only on the lengths of the two slices, which must match.
|
||||||
|
func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
||||||
|
if len(scalars) != len(points) {
|
||||||
|
panic("edwards25519: called MultiScalarMult with different size inputs")
|
||||||
|
}
|
||||||
|
checkInitialized(points...)
|
||||||
|
|
||||||
|
// Proceed as in the single-base case, but share doublings
|
||||||
|
// between each point in the multiscalar equation.
|
||||||
|
|
||||||
|
// Build lookup tables for each point
|
||||||
|
tables := make([]projLookupTable, len(points))
|
||||||
|
for i := range tables {
|
||||||
|
tables[i].FromP3(points[i])
|
||||||
|
}
|
||||||
|
// Compute signed radix-16 digits for each scalar
|
||||||
|
digits := make([][64]int8, len(scalars))
|
||||||
|
for i := range digits {
|
||||||
|
digits[i] = scalars[i].signedRadix16()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap first loop iteration to save computing 16*identity
|
||||||
|
multiple := &projCached{}
|
||||||
|
tmp1 := &projP1xP1{}
|
||||||
|
tmp2 := &projP2{}
|
||||||
|
// Lookup-and-add the appropriate multiple of each input point
|
||||||
|
for j := range tables {
|
||||||
|
tables[j].SelectInto(multiple, digits[j][63])
|
||||||
|
tmp1.Add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords
|
||||||
|
v.fromP1xP1(tmp1) // update v
|
||||||
|
}
|
||||||
|
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||||
|
for i := 62; i >= 0; i-- {
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||||
|
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
||||||
|
// Lookup-and-add the appropriate multiple of each input point
|
||||||
|
for j := range tables {
|
||||||
|
tables[j].SelectInto(multiple, digits[j][i])
|
||||||
|
tmp1.Add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords
|
||||||
|
v.fromP1xP1(tmp1) // update v
|
||||||
|
}
|
||||||
|
tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarTimeMultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
|
||||||
|
//
|
||||||
|
// Execution time depends on the inputs.
|
||||||
|
func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Point {
|
||||||
|
if len(scalars) != len(points) {
|
||||||
|
panic("edwards25519: called VarTimeMultiScalarMult with different size inputs")
|
||||||
|
}
|
||||||
|
checkInitialized(points...)
|
||||||
|
|
||||||
|
// Generalize double-base NAF computation to arbitrary sizes.
|
||||||
|
// Here all the points are dynamic, so we only use the smaller
|
||||||
|
// tables.
|
||||||
|
|
||||||
|
// Build lookup tables for each point
|
||||||
|
tables := make([]nafLookupTable5, len(points))
|
||||||
|
for i := range tables {
|
||||||
|
tables[i].FromP3(points[i])
|
||||||
|
}
|
||||||
|
// Compute a NAF for each scalar
|
||||||
|
nafs := make([][256]int8, len(scalars))
|
||||||
|
for i := range nafs {
|
||||||
|
nafs[i] = scalars[i].nonAdjacentForm(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
multiple := &projCached{}
|
||||||
|
tmp1 := &projP1xP1{}
|
||||||
|
tmp2 := &projP2{}
|
||||||
|
tmp2.Zero()
|
||||||
|
|
||||||
|
// Move from high to low bits, doubling the accumulator
|
||||||
|
// at each iteration and checking whether there is a nonzero
|
||||||
|
// coefficient to look up a multiple of.
|
||||||
|
//
|
||||||
|
// Skip trying to find the first nonzero coefficent, because
|
||||||
|
// searching might be more work than a few extra doublings.
|
||||||
|
for i := 255; i >= 0; i-- {
|
||||||
|
tmp1.Double(tmp2)
|
||||||
|
|
||||||
|
for j := range nafs {
|
||||||
|
if nafs[j][i] > 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
tables[j].SelectInto(multiple, nafs[j][i])
|
||||||
|
tmp1.Add(v, multiple)
|
||||||
|
} else if nafs[j][i] < 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
tables[j].SelectInto(multiple, -nafs[j][i])
|
||||||
|
tmp1.Sub(v, multiple)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp2.FromP1xP1(tmp1)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.fromP2(tmp2)
|
||||||
|
return v
|
||||||
|
}
|
419
vendor/filippo.io/edwards25519/field/fe.go
generated
vendored
Normal file
419
vendor/filippo.io/edwards25519/field/fe.go
generated
vendored
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package field implements fast arithmetic modulo 2^255-19.
|
||||||
|
package field
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Element represents an element of the field GF(2^255-19). Note that this
|
||||||
|
// is not a cryptographically secure group, and should only be used to interact
|
||||||
|
// with edwards25519.Point coordinates.
|
||||||
|
//
|
||||||
|
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||||
|
// are allowed to alias.
|
||||||
|
//
|
||||||
|
// The zero value is a valid zero element.
|
||||||
|
type Element struct {
|
||||||
|
// An element t represents the integer
|
||||||
|
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
|
||||||
|
//
|
||||||
|
// Between operations, all limbs are expected to be lower than 2^52.
|
||||||
|
l0 uint64
|
||||||
|
l1 uint64
|
||||||
|
l2 uint64
|
||||||
|
l3 uint64
|
||||||
|
l4 uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
const maskLow51Bits uint64 = (1 << 51) - 1
|
||||||
|
|
||||||
|
var feZero = &Element{0, 0, 0, 0, 0}
|
||||||
|
|
||||||
|
// Zero sets v = 0, and returns v.
|
||||||
|
func (v *Element) Zero() *Element {
|
||||||
|
*v = *feZero
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
var feOne = &Element{1, 0, 0, 0, 0}
|
||||||
|
|
||||||
|
// One sets v = 1, and returns v.
|
||||||
|
func (v *Element) One() *Element {
|
||||||
|
*v = *feOne
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// reduce reduces v modulo 2^255 - 19 and returns it.
|
||||||
|
func (v *Element) reduce() *Element {
|
||||||
|
v.carryPropagate()
|
||||||
|
|
||||||
|
// After the light reduction we now have a field element representation
|
||||||
|
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
|
||||||
|
|
||||||
|
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
|
||||||
|
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
|
||||||
|
c := (v.l0 + 19) >> 51
|
||||||
|
c = (v.l1 + c) >> 51
|
||||||
|
c = (v.l2 + c) >> 51
|
||||||
|
c = (v.l3 + c) >> 51
|
||||||
|
c = (v.l4 + c) >> 51
|
||||||
|
|
||||||
|
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
|
||||||
|
// effectively applying the reduction identity to the carry.
|
||||||
|
v.l0 += 19 * c
|
||||||
|
|
||||||
|
v.l1 += v.l0 >> 51
|
||||||
|
v.l0 = v.l0 & maskLow51Bits
|
||||||
|
v.l2 += v.l1 >> 51
|
||||||
|
v.l1 = v.l1 & maskLow51Bits
|
||||||
|
v.l3 += v.l2 >> 51
|
||||||
|
v.l2 = v.l2 & maskLow51Bits
|
||||||
|
v.l4 += v.l3 >> 51
|
||||||
|
v.l3 = v.l3 & maskLow51Bits
|
||||||
|
// no additional carry
|
||||||
|
v.l4 = v.l4 & maskLow51Bits
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sets v = a + b, and returns v.
|
||||||
|
func (v *Element) Add(a, b *Element) *Element {
|
||||||
|
v.l0 = a.l0 + b.l0
|
||||||
|
v.l1 = a.l1 + b.l1
|
||||||
|
v.l2 = a.l2 + b.l2
|
||||||
|
v.l3 = a.l3 + b.l3
|
||||||
|
v.l4 = a.l4 + b.l4
|
||||||
|
// Using the generic implementation here is actually faster than the
|
||||||
|
// assembly. Probably because the body of this function is so simple that
|
||||||
|
// the compiler can figure out better optimizations by inlining the carry
|
||||||
|
// propagation.
|
||||||
|
return v.carryPropagateGeneric()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract sets v = a - b, and returns v.
|
||||||
|
func (v *Element) Subtract(a, b *Element) *Element {
|
||||||
|
// We first add 2 * p, to guarantee the subtraction won't underflow, and
|
||||||
|
// then subtract b (which can be up to 2^255 + 2^13 * 19).
|
||||||
|
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
|
||||||
|
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
|
||||||
|
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
|
||||||
|
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
|
||||||
|
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
|
||||||
|
return v.carryPropagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negate sets v = -a, and returns v.
|
||||||
|
func (v *Element) Negate(a *Element) *Element {
|
||||||
|
return v.Subtract(feZero, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert sets v = 1/z mod p, and returns v.
|
||||||
|
//
|
||||||
|
// If z == 0, Invert returns v = 0.
|
||||||
|
func (v *Element) Invert(z *Element) *Element {
|
||||||
|
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
|
||||||
|
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
|
||||||
|
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element
|
||||||
|
|
||||||
|
z2.Square(z) // 2
|
||||||
|
t.Square(&z2) // 4
|
||||||
|
t.Square(&t) // 8
|
||||||
|
z9.Multiply(&t, z) // 9
|
||||||
|
z11.Multiply(&z9, &z2) // 11
|
||||||
|
t.Square(&z11) // 22
|
||||||
|
z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_5_0) // 2^6 - 2^1
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
t.Square(&t) // 2^10 - 2^5
|
||||||
|
}
|
||||||
|
z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_10_0) // 2^11 - 2^1
|
||||||
|
for i := 0; i < 9; i++ {
|
||||||
|
t.Square(&t) // 2^20 - 2^10
|
||||||
|
}
|
||||||
|
z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_20_0) // 2^21 - 2^1
|
||||||
|
for i := 0; i < 19; i++ {
|
||||||
|
t.Square(&t) // 2^40 - 2^20
|
||||||
|
}
|
||||||
|
t.Multiply(&t, &z2_20_0) // 2^40 - 2^0
|
||||||
|
|
||||||
|
t.Square(&t) // 2^41 - 2^1
|
||||||
|
for i := 0; i < 9; i++ {
|
||||||
|
t.Square(&t) // 2^50 - 2^10
|
||||||
|
}
|
||||||
|
z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_50_0) // 2^51 - 2^1
|
||||||
|
for i := 0; i < 49; i++ {
|
||||||
|
t.Square(&t) // 2^100 - 2^50
|
||||||
|
}
|
||||||
|
z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_100_0) // 2^101 - 2^1
|
||||||
|
for i := 0; i < 99; i++ {
|
||||||
|
t.Square(&t) // 2^200 - 2^100
|
||||||
|
}
|
||||||
|
t.Multiply(&t, &z2_100_0) // 2^200 - 2^0
|
||||||
|
|
||||||
|
t.Square(&t) // 2^201 - 2^1
|
||||||
|
for i := 0; i < 49; i++ {
|
||||||
|
t.Square(&t) // 2^250 - 2^50
|
||||||
|
}
|
||||||
|
t.Multiply(&t, &z2_50_0) // 2^250 - 2^0
|
||||||
|
|
||||||
|
t.Square(&t) // 2^251 - 2^1
|
||||||
|
t.Square(&t) // 2^252 - 2^2
|
||||||
|
t.Square(&t) // 2^253 - 2^3
|
||||||
|
t.Square(&t) // 2^254 - 2^4
|
||||||
|
t.Square(&t) // 2^255 - 2^5
|
||||||
|
|
||||||
|
return v.Multiply(&t, &z11) // 2^255 - 21
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets v = a, and returns v.
|
||||||
|
func (v *Element) Set(a *Element) *Element {
|
||||||
|
*v = *a
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBytes sets v to x, where x is a 32-byte little-endian encoding. If x is
|
||||||
|
// not of the right length, SetUniformBytes returns nil and an error, and the
|
||||||
|
// receiver is unchanged.
|
||||||
|
//
|
||||||
|
// Consistent with RFC 7748, the most significant bit (the high bit of the
|
||||||
|
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
|
||||||
|
// are accepted. Note that this is laxer than specified by RFC 8032.
|
||||||
|
func (v *Element) SetBytes(x []byte) (*Element, error) {
|
||||||
|
if len(x) != 32 {
|
||||||
|
return nil, errors.New("edwards25519: invalid field element input size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
|
||||||
|
v.l0 = binary.LittleEndian.Uint64(x[0:8])
|
||||||
|
v.l0 &= maskLow51Bits
|
||||||
|
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
|
||||||
|
v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3
|
||||||
|
v.l1 &= maskLow51Bits
|
||||||
|
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
|
||||||
|
v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6
|
||||||
|
v.l2 &= maskLow51Bits
|
||||||
|
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
|
||||||
|
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
|
||||||
|
v.l3 &= maskLow51Bits
|
||||||
|
// Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51).
|
||||||
|
// Note: not bytes 25:33, shift 4, to avoid overread.
|
||||||
|
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
|
||||||
|
v.l4 &= maskLow51Bits
|
||||||
|
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the canonical 32-byte little-endian encoding of v.
|
||||||
|
func (v *Element) Bytes() []byte {
|
||||||
|
// This function is outlined to make the allocations inline in the caller
|
||||||
|
// rather than happen on the heap.
|
||||||
|
var out [32]byte
|
||||||
|
return v.bytes(&out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Element) bytes(out *[32]byte) []byte {
|
||||||
|
t := *v
|
||||||
|
t.reduce()
|
||||||
|
|
||||||
|
var buf [8]byte
|
||||||
|
for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} {
|
||||||
|
bitsOffset := i * 51
|
||||||
|
binary.LittleEndian.PutUint64(buf[:], l<<uint(bitsOffset%8))
|
||||||
|
for i, bb := range buf {
|
||||||
|
off := bitsOffset/8 + i
|
||||||
|
if off >= len(out) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out[off] |= bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns 1 if v and u are equal, and 0 otherwise.
|
||||||
|
func (v *Element) Equal(u *Element) int {
|
||||||
|
sa, sv := u.Bytes(), v.Bytes()
|
||||||
|
return subtle.ConstantTimeCompare(sa, sv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise.
|
||||||
|
func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) }
|
||||||
|
|
||||||
|
// Select sets v to a if cond == 1, and to b if cond == 0.
|
||||||
|
func (v *Element) Select(a, b *Element, cond int) *Element {
|
||||||
|
m := mask64Bits(cond)
|
||||||
|
v.l0 = (m & a.l0) | (^m & b.l0)
|
||||||
|
v.l1 = (m & a.l1) | (^m & b.l1)
|
||||||
|
v.l2 = (m & a.l2) | (^m & b.l2)
|
||||||
|
v.l3 = (m & a.l3) | (^m & b.l3)
|
||||||
|
v.l4 = (m & a.l4) | (^m & b.l4)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
|
||||||
|
func (v *Element) Swap(u *Element, cond int) {
|
||||||
|
m := mask64Bits(cond)
|
||||||
|
t := m & (v.l0 ^ u.l0)
|
||||||
|
v.l0 ^= t
|
||||||
|
u.l0 ^= t
|
||||||
|
t = m & (v.l1 ^ u.l1)
|
||||||
|
v.l1 ^= t
|
||||||
|
u.l1 ^= t
|
||||||
|
t = m & (v.l2 ^ u.l2)
|
||||||
|
v.l2 ^= t
|
||||||
|
u.l2 ^= t
|
||||||
|
t = m & (v.l3 ^ u.l3)
|
||||||
|
v.l3 ^= t
|
||||||
|
u.l3 ^= t
|
||||||
|
t = m & (v.l4 ^ u.l4)
|
||||||
|
v.l4 ^= t
|
||||||
|
u.l4 ^= t
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNegative returns 1 if v is negative, and 0 otherwise.
|
||||||
|
func (v *Element) IsNegative() int {
|
||||||
|
return int(v.Bytes()[0] & 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Absolute sets v to |u|, and returns v.
|
||||||
|
func (v *Element) Absolute(u *Element) *Element {
|
||||||
|
return v.Select(new(Element).Negate(u), u, u.IsNegative())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply sets v = x * y, and returns v.
|
||||||
|
func (v *Element) Multiply(x, y *Element) *Element {
|
||||||
|
feMul(v, x, y)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Square sets v = x * x, and returns v.
|
||||||
|
func (v *Element) Square(x *Element) *Element {
|
||||||
|
feSquare(v, x)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mult32 sets v = x * y, and returns v.
|
||||||
|
func (v *Element) Mult32(x *Element, y uint32) *Element {
|
||||||
|
x0lo, x0hi := mul51(x.l0, y)
|
||||||
|
x1lo, x1hi := mul51(x.l1, y)
|
||||||
|
x2lo, x2hi := mul51(x.l2, y)
|
||||||
|
x3lo, x3hi := mul51(x.l3, y)
|
||||||
|
x4lo, x4hi := mul51(x.l4, y)
|
||||||
|
v.l0 = x0lo + 19*x4hi // carried over per the reduction identity
|
||||||
|
v.l1 = x1lo + x0hi
|
||||||
|
v.l2 = x2lo + x1hi
|
||||||
|
v.l3 = x3lo + x2hi
|
||||||
|
v.l4 = x4lo + x3hi
|
||||||
|
// The hi portions are going to be only 32 bits, plus any previous excess,
|
||||||
|
// so we can skip the carry propagation.
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// mul51 returns lo + hi * 2⁵¹ = a * b.
|
||||||
|
func mul51(a uint64, b uint32) (lo uint64, hi uint64) {
|
||||||
|
mh, ml := bits.Mul64(a, uint64(b))
|
||||||
|
lo = ml & maskLow51Bits
|
||||||
|
hi = (mh << 13) | (ml >> 51)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
|
||||||
|
func (v *Element) Pow22523(x *Element) *Element {
|
||||||
|
var t0, t1, t2 Element
|
||||||
|
|
||||||
|
t0.Square(x) // x^2
|
||||||
|
t1.Square(&t0) // x^4
|
||||||
|
t1.Square(&t1) // x^8
|
||||||
|
t1.Multiply(x, &t1) // x^9
|
||||||
|
t0.Multiply(&t0, &t1) // x^11
|
||||||
|
t0.Square(&t0) // x^22
|
||||||
|
t0.Multiply(&t1, &t0) // x^31
|
||||||
|
t1.Square(&t0) // x^62
|
||||||
|
for i := 1; i < 5; i++ { // x^992
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1
|
||||||
|
t1.Square(&t0) // 2^11 - 2
|
||||||
|
for i := 1; i < 10; i++ { // 2^20 - 2^10
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t1, &t0) // 2^20 - 1
|
||||||
|
t2.Square(&t1) // 2^21 - 2
|
||||||
|
for i := 1; i < 20; i++ { // 2^40 - 2^20
|
||||||
|
t2.Square(&t2)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t2, &t1) // 2^40 - 1
|
||||||
|
t1.Square(&t1) // 2^41 - 2
|
||||||
|
for i := 1; i < 10; i++ { // 2^50 - 2^10
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t0.Multiply(&t1, &t0) // 2^50 - 1
|
||||||
|
t1.Square(&t0) // 2^51 - 2
|
||||||
|
for i := 1; i < 50; i++ { // 2^100 - 2^50
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t1, &t0) // 2^100 - 1
|
||||||
|
t2.Square(&t1) // 2^101 - 2
|
||||||
|
for i := 1; i < 100; i++ { // 2^200 - 2^100
|
||||||
|
t2.Square(&t2)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t2, &t1) // 2^200 - 1
|
||||||
|
t1.Square(&t1) // 2^201 - 2
|
||||||
|
for i := 1; i < 50; i++ { // 2^250 - 2^50
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t0.Multiply(&t1, &t0) // 2^250 - 1
|
||||||
|
t0.Square(&t0) // 2^251 - 2
|
||||||
|
t0.Square(&t0) // 2^252 - 4
|
||||||
|
return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
|
||||||
|
var sqrtM1 = &Element{1718705420411056, 234908883556509,
|
||||||
|
2233514472574048, 2117202627021982, 765476049583133}
|
||||||
|
|
||||||
|
// SqrtRatio sets r to the non-negative square root of the ratio of u and v.
|
||||||
|
//
|
||||||
|
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
|
||||||
|
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
|
||||||
|
// and returns r and 0.
|
||||||
|
func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) {
|
||||||
|
var a, b Element
|
||||||
|
|
||||||
|
// r = (u * v3) * (u * v7)^((p-5)/8)
|
||||||
|
v2 := a.Square(v)
|
||||||
|
uv3 := b.Multiply(u, b.Multiply(v2, v))
|
||||||
|
uv7 := a.Multiply(uv3, a.Square(v2))
|
||||||
|
r.Multiply(uv3, r.Pow22523(uv7))
|
||||||
|
|
||||||
|
check := a.Multiply(v, a.Square(r)) // check = v * r^2
|
||||||
|
|
||||||
|
uNeg := b.Negate(u)
|
||||||
|
correctSignSqrt := check.Equal(u)
|
||||||
|
flippedSignSqrt := check.Equal(uNeg)
|
||||||
|
flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1))
|
||||||
|
|
||||||
|
rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r
|
||||||
|
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
|
||||||
|
r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI)
|
||||||
|
|
||||||
|
r.Absolute(r) // Choose the nonnegative square root.
|
||||||
|
return r, correctSignSqrt | flippedSignSqrt
|
||||||
|
}
|
13
vendor/filippo.io/edwards25519/field/fe_amd64.go
generated
vendored
Normal file
13
vendor/filippo.io/edwards25519/field/fe_amd64.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||||
|
|
||||||
|
// +build amd64,gc,!purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
// feMul sets out = a * b. It works like feMulGeneric.
|
||||||
|
//go:noescape
|
||||||
|
func feMul(out *Element, a *Element, b *Element)
|
||||||
|
|
||||||
|
// feSquare sets out = a * a. It works like feSquareGeneric.
|
||||||
|
//go:noescape
|
||||||
|
func feSquare(out *Element, a *Element)
|
378
vendor/filippo.io/edwards25519/field/fe_amd64.s
generated
vendored
Normal file
378
vendor/filippo.io/edwards25519/field/fe_amd64.s
generated
vendored
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||||
|
|
||||||
|
// +build amd64,gc,!purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func feMul(out *Element, a *Element, b *Element)
|
||||||
|
TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||||
|
MOVQ a+8(FP), CX
|
||||||
|
MOVQ b+16(FP), BX
|
||||||
|
|
||||||
|
// r0 = a0×b0
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
MOVQ AX, DI
|
||||||
|
MOVQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a1×b4
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a2×b3
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a3×b2
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a4×b1
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r1 = a0×b1
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
MOVQ AX, R9
|
||||||
|
MOVQ DX, R8
|
||||||
|
|
||||||
|
// r1 += a1×b0
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r1 += 19×a2×b4
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r1 += 19×a3×b3
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r1 += 19×a4×b2
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r2 = a0×b2
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
MOVQ AX, R11
|
||||||
|
MOVQ DX, R10
|
||||||
|
|
||||||
|
// r2 += a1×b1
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r2 += a2×b0
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r2 += 19×a3×b4
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r2 += 19×a4×b3
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r3 = a0×b3
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
MOVQ AX, R13
|
||||||
|
MOVQ DX, R12
|
||||||
|
|
||||||
|
// r3 += a1×b2
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r3 += a2×b1
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r3 += a3×b0
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r3 += 19×a4×b4
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r4 = a0×b4
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
MOVQ AX, R15
|
||||||
|
MOVQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a1×b3
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a2×b2
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a3×b1
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a4×b0
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// First reduction chain
|
||||||
|
MOVQ $0x0007ffffffffffff, AX
|
||||||
|
SHLQ $0x0d, DI, SI
|
||||||
|
SHLQ $0x0d, R9, R8
|
||||||
|
SHLQ $0x0d, R11, R10
|
||||||
|
SHLQ $0x0d, R13, R12
|
||||||
|
SHLQ $0x0d, R15, R14
|
||||||
|
ANDQ AX, DI
|
||||||
|
IMUL3Q $0x13, R14, R14
|
||||||
|
ADDQ R14, DI
|
||||||
|
ANDQ AX, R9
|
||||||
|
ADDQ SI, R9
|
||||||
|
ANDQ AX, R11
|
||||||
|
ADDQ R8, R11
|
||||||
|
ANDQ AX, R13
|
||||||
|
ADDQ R10, R13
|
||||||
|
ANDQ AX, R15
|
||||||
|
ADDQ R12, R15
|
||||||
|
|
||||||
|
// Second reduction chain (carryPropagate)
|
||||||
|
MOVQ DI, SI
|
||||||
|
SHRQ $0x33, SI
|
||||||
|
MOVQ R9, R8
|
||||||
|
SHRQ $0x33, R8
|
||||||
|
MOVQ R11, R10
|
||||||
|
SHRQ $0x33, R10
|
||||||
|
MOVQ R13, R12
|
||||||
|
SHRQ $0x33, R12
|
||||||
|
MOVQ R15, R14
|
||||||
|
SHRQ $0x33, R14
|
||||||
|
ANDQ AX, DI
|
||||||
|
IMUL3Q $0x13, R14, R14
|
||||||
|
ADDQ R14, DI
|
||||||
|
ANDQ AX, R9
|
||||||
|
ADDQ SI, R9
|
||||||
|
ANDQ AX, R11
|
||||||
|
ADDQ R8, R11
|
||||||
|
ANDQ AX, R13
|
||||||
|
ADDQ R10, R13
|
||||||
|
ANDQ AX, R15
|
||||||
|
ADDQ R12, R15
|
||||||
|
|
||||||
|
// Store output
|
||||||
|
MOVQ out+0(FP), AX
|
||||||
|
MOVQ DI, (AX)
|
||||||
|
MOVQ R9, 8(AX)
|
||||||
|
MOVQ R11, 16(AX)
|
||||||
|
MOVQ R13, 24(AX)
|
||||||
|
MOVQ R15, 32(AX)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func feSquare(out *Element, a *Element)
|
||||||
|
TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||||
|
MOVQ a+8(FP), CX
|
||||||
|
|
||||||
|
// r0 = l0×l0
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ (CX)
|
||||||
|
MOVQ AX, SI
|
||||||
|
MOVQ DX, BX
|
||||||
|
|
||||||
|
// r0 += 38×l1×l4
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, SI
|
||||||
|
ADCQ DX, BX
|
||||||
|
|
||||||
|
// r0 += 38×l2×l3
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX, SI
|
||||||
|
ADCQ DX, BX
|
||||||
|
|
||||||
|
// r1 = 2×l0×l1
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
MOVQ AX, R8
|
||||||
|
MOVQ DX, DI
|
||||||
|
|
||||||
|
// r1 += 38×l2×l4
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, R8
|
||||||
|
ADCQ DX, DI
|
||||||
|
|
||||||
|
// r1 += 19×l3×l3
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX, R8
|
||||||
|
ADCQ DX, DI
|
||||||
|
|
||||||
|
// r2 = 2×l0×l2
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
MOVQ AX, R10
|
||||||
|
MOVQ DX, R9
|
||||||
|
|
||||||
|
// r2 += l1×l1
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
ADDQ AX, R10
|
||||||
|
ADCQ DX, R9
|
||||||
|
|
||||||
|
// r2 += 38×l3×l4
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, R10
|
||||||
|
ADCQ DX, R9
|
||||||
|
|
||||||
|
// r3 = 2×l0×l3
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
MOVQ AX, R12
|
||||||
|
MOVQ DX, R11
|
||||||
|
|
||||||
|
// r3 += 2×l1×l2
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x02, AX, AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
ADDQ AX, R12
|
||||||
|
ADCQ DX, R11
|
||||||
|
|
||||||
|
// r3 += 19×l4×l4
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, R12
|
||||||
|
ADCQ DX, R11
|
||||||
|
|
||||||
|
// r4 = 2×l0×l4
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
MOVQ AX, R14
|
||||||
|
MOVQ DX, R13
|
||||||
|
|
||||||
|
// r4 += 2×l1×l3
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x02, AX, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX, R14
|
||||||
|
ADCQ DX, R13
|
||||||
|
|
||||||
|
// r4 += l2×l2
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
ADDQ AX, R14
|
||||||
|
ADCQ DX, R13
|
||||||
|
|
||||||
|
// First reduction chain
|
||||||
|
MOVQ $0x0007ffffffffffff, AX
|
||||||
|
SHLQ $0x0d, SI, BX
|
||||||
|
SHLQ $0x0d, R8, DI
|
||||||
|
SHLQ $0x0d, R10, R9
|
||||||
|
SHLQ $0x0d, R12, R11
|
||||||
|
SHLQ $0x0d, R14, R13
|
||||||
|
ANDQ AX, SI
|
||||||
|
IMUL3Q $0x13, R13, R13
|
||||||
|
ADDQ R13, SI
|
||||||
|
ANDQ AX, R8
|
||||||
|
ADDQ BX, R8
|
||||||
|
ANDQ AX, R10
|
||||||
|
ADDQ DI, R10
|
||||||
|
ANDQ AX, R12
|
||||||
|
ADDQ R9, R12
|
||||||
|
ANDQ AX, R14
|
||||||
|
ADDQ R11, R14
|
||||||
|
|
||||||
|
// Second reduction chain (carryPropagate)
|
||||||
|
MOVQ SI, BX
|
||||||
|
SHRQ $0x33, BX
|
||||||
|
MOVQ R8, DI
|
||||||
|
SHRQ $0x33, DI
|
||||||
|
MOVQ R10, R9
|
||||||
|
SHRQ $0x33, R9
|
||||||
|
MOVQ R12, R11
|
||||||
|
SHRQ $0x33, R11
|
||||||
|
MOVQ R14, R13
|
||||||
|
SHRQ $0x33, R13
|
||||||
|
ANDQ AX, SI
|
||||||
|
IMUL3Q $0x13, R13, R13
|
||||||
|
ADDQ R13, SI
|
||||||
|
ANDQ AX, R8
|
||||||
|
ADDQ BX, R8
|
||||||
|
ANDQ AX, R10
|
||||||
|
ADDQ DI, R10
|
||||||
|
ANDQ AX, R12
|
||||||
|
ADDQ R9, R12
|
||||||
|
ANDQ AX, R14
|
||||||
|
ADDQ R11, R14
|
||||||
|
|
||||||
|
// Store output
|
||||||
|
MOVQ out+0(FP), AX
|
||||||
|
MOVQ SI, (AX)
|
||||||
|
MOVQ R8, 8(AX)
|
||||||
|
MOVQ R10, 16(AX)
|
||||||
|
MOVQ R12, 24(AX)
|
||||||
|
MOVQ R14, 32(AX)
|
||||||
|
RET
|
12
vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go
generated
vendored
Normal file
12
vendor/filippo.io/edwards25519/field/fe_amd64_noasm.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !amd64 || !gc || purego
|
||||||
|
// +build !amd64 !gc purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
|
||||||
|
|
||||||
|
func feSquare(v, x *Element) { feSquareGeneric(v, x) }
|
16
vendor/filippo.io/edwards25519/field/fe_arm64.go
generated
vendored
Normal file
16
vendor/filippo.io/edwards25519/field/fe_arm64.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build arm64 && gc && !purego
|
||||||
|
// +build arm64,gc,!purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func carryPropagate(v *Element)
|
||||||
|
|
||||||
|
func (v *Element) carryPropagate() *Element {
|
||||||
|
carryPropagate(v)
|
||||||
|
return v
|
||||||
|
}
|
42
vendor/filippo.io/edwards25519/field/fe_arm64.s
generated
vendored
Normal file
42
vendor/filippo.io/edwards25519/field/fe_arm64.s
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build arm64,gc,!purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// carryPropagate works exactly like carryPropagateGeneric and uses the
|
||||||
|
// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but
|
||||||
|
// avoids loading R0-R4 twice and uses LDP and STP.
|
||||||
|
//
|
||||||
|
// See https://golang.org/issues/43145 for the main compiler issue.
|
||||||
|
//
|
||||||
|
// func carryPropagate(v *Element)
|
||||||
|
TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8
|
||||||
|
MOVD v+0(FP), R20
|
||||||
|
|
||||||
|
LDP 0(R20), (R0, R1)
|
||||||
|
LDP 16(R20), (R2, R3)
|
||||||
|
MOVD 32(R20), R4
|
||||||
|
|
||||||
|
AND $0x7ffffffffffff, R0, R10
|
||||||
|
AND $0x7ffffffffffff, R1, R11
|
||||||
|
AND $0x7ffffffffffff, R2, R12
|
||||||
|
AND $0x7ffffffffffff, R3, R13
|
||||||
|
AND $0x7ffffffffffff, R4, R14
|
||||||
|
|
||||||
|
ADD R0>>51, R11, R11
|
||||||
|
ADD R1>>51, R12, R12
|
||||||
|
ADD R2>>51, R13, R13
|
||||||
|
ADD R3>>51, R14, R14
|
||||||
|
// R4>>51 * 19 + R10 -> R10
|
||||||
|
LSR $51, R4, R21
|
||||||
|
MOVD $19, R22
|
||||||
|
MADD R22, R10, R21, R10
|
||||||
|
|
||||||
|
STP (R10, R11), 0(R20)
|
||||||
|
STP (R12, R13), 16(R20)
|
||||||
|
MOVD R14, 32(R20)
|
||||||
|
|
||||||
|
RET
|
12
vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go
generated
vendored
Normal file
12
vendor/filippo.io/edwards25519/field/fe_arm64_noasm.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !arm64 || !gc || purego
|
||||||
|
// +build !arm64 !gc purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
func (v *Element) carryPropagate() *Element {
|
||||||
|
return v.carryPropagateGeneric()
|
||||||
|
}
|
264
vendor/filippo.io/edwards25519/field/fe_generic.go
generated
vendored
Normal file
264
vendor/filippo.io/edwards25519/field/fe_generic.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
import "math/bits"
|
||||||
|
|
||||||
|
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
|
||||||
|
// bits.Mul64 and bits.Add64 intrinsics.
|
||||||
|
type uint128 struct {
|
||||||
|
lo, hi uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// mul64 returns a * b.
|
||||||
|
func mul64(a, b uint64) uint128 {
|
||||||
|
hi, lo := bits.Mul64(a, b)
|
||||||
|
return uint128{lo, hi}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addMul64 returns v + a * b.
|
||||||
|
func addMul64(v uint128, a, b uint64) uint128 {
|
||||||
|
hi, lo := bits.Mul64(a, b)
|
||||||
|
lo, c := bits.Add64(lo, v.lo, 0)
|
||||||
|
hi, _ = bits.Add64(hi, v.hi, c)
|
||||||
|
return uint128{lo, hi}
|
||||||
|
}
|
||||||
|
|
||||||
|
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
|
||||||
|
func shiftRightBy51(a uint128) uint64 {
|
||||||
|
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
||||||
|
}
|
||||||
|
|
||||||
|
func feMulGeneric(v, a, b *Element) {
|
||||||
|
a0 := a.l0
|
||||||
|
a1 := a.l1
|
||||||
|
a2 := a.l2
|
||||||
|
a3 := a.l3
|
||||||
|
a4 := a.l4
|
||||||
|
|
||||||
|
b0 := b.l0
|
||||||
|
b1 := b.l1
|
||||||
|
b2 := b.l2
|
||||||
|
b3 := b.l3
|
||||||
|
b4 := b.l4
|
||||||
|
|
||||||
|
// Limb multiplication works like pen-and-paper columnar multiplication, but
|
||||||
|
// with 51-bit limbs instead of digits.
|
||||||
|
//
|
||||||
|
// a4 a3 a2 a1 a0 x
|
||||||
|
// b4 b3 b2 b1 b0 =
|
||||||
|
// ------------------------
|
||||||
|
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||||
|
// a4b1 a3b1 a2b1 a1b1 a0b1 +
|
||||||
|
// a4b2 a3b2 a2b2 a1b2 a0b2 +
|
||||||
|
// a4b3 a3b3 a2b3 a1b3 a0b3 +
|
||||||
|
// a4b4 a3b4 a2b4 a1b4 a0b4 =
|
||||||
|
// ----------------------------------------------
|
||||||
|
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
|
||||||
|
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
|
||||||
|
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
|
||||||
|
//
|
||||||
|
// Reduction can be carried out simultaneously to multiplication. For
|
||||||
|
// example, we do not compute r5: whenever the result of a multiplication
|
||||||
|
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
|
||||||
|
//
|
||||||
|
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||||
|
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
|
||||||
|
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
|
||||||
|
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
|
||||||
|
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
|
||||||
|
// --------------------------------------
|
||||||
|
// r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// Finally we add up the columns into wide, overlapping limbs.
|
||||||
|
|
||||||
|
a1_19 := a1 * 19
|
||||||
|
a2_19 := a2 * 19
|
||||||
|
a3_19 := a3 * 19
|
||||||
|
a4_19 := a4 * 19
|
||||||
|
|
||||||
|
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||||
|
r0 := mul64(a0, b0)
|
||||||
|
r0 = addMul64(r0, a1_19, b4)
|
||||||
|
r0 = addMul64(r0, a2_19, b3)
|
||||||
|
r0 = addMul64(r0, a3_19, b2)
|
||||||
|
r0 = addMul64(r0, a4_19, b1)
|
||||||
|
|
||||||
|
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||||
|
r1 := mul64(a0, b1)
|
||||||
|
r1 = addMul64(r1, a1, b0)
|
||||||
|
r1 = addMul64(r1, a2_19, b4)
|
||||||
|
r1 = addMul64(r1, a3_19, b3)
|
||||||
|
r1 = addMul64(r1, a4_19, b2)
|
||||||
|
|
||||||
|
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||||
|
r2 := mul64(a0, b2)
|
||||||
|
r2 = addMul64(r2, a1, b1)
|
||||||
|
r2 = addMul64(r2, a2, b0)
|
||||||
|
r2 = addMul64(r2, a3_19, b4)
|
||||||
|
r2 = addMul64(r2, a4_19, b3)
|
||||||
|
|
||||||
|
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||||
|
r3 := mul64(a0, b3)
|
||||||
|
r3 = addMul64(r3, a1, b2)
|
||||||
|
r3 = addMul64(r3, a2, b1)
|
||||||
|
r3 = addMul64(r3, a3, b0)
|
||||||
|
r3 = addMul64(r3, a4_19, b4)
|
||||||
|
|
||||||
|
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||||
|
r4 := mul64(a0, b4)
|
||||||
|
r4 = addMul64(r4, a1, b3)
|
||||||
|
r4 = addMul64(r4, a2, b2)
|
||||||
|
r4 = addMul64(r4, a3, b1)
|
||||||
|
r4 = addMul64(r4, a4, b0)
|
||||||
|
|
||||||
|
// After the multiplication, we need to reduce (carry) the five coefficients
|
||||||
|
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
||||||
|
// to respect the Element invariant.
|
||||||
|
//
|
||||||
|
// Overall, the reduction works the same as carryPropagate, except with
|
||||||
|
// wider inputs: we take the carry for each coefficient by shifting it right
|
||||||
|
// by 51, and add it to the limb above it. The top carry is multiplied by 19
|
||||||
|
// according to the reduction identity and added to the lowest limb.
|
||||||
|
//
|
||||||
|
// The largest coefficient (r0) will be at most 111 bits, which guarantees
|
||||||
|
// that all carries are at most 111 - 51 = 60 bits, which fits in a uint64.
|
||||||
|
//
|
||||||
|
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||||
|
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
|
||||||
|
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
|
||||||
|
// r0 < 2⁷ × 2⁵² × 2⁵²
|
||||||
|
// r0 < 2¹¹¹
|
||||||
|
//
|
||||||
|
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
|
||||||
|
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and
|
||||||
|
// allows us to easily apply the reduction identity.
|
||||||
|
//
|
||||||
|
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||||
|
// r4 < 5 × 2⁵² × 2⁵²
|
||||||
|
// r4 < 2¹⁰⁷
|
||||||
|
//
|
||||||
|
|
||||||
|
c0 := shiftRightBy51(r0)
|
||||||
|
c1 := shiftRightBy51(r1)
|
||||||
|
c2 := shiftRightBy51(r2)
|
||||||
|
c3 := shiftRightBy51(r3)
|
||||||
|
c4 := shiftRightBy51(r4)
|
||||||
|
|
||||||
|
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||||
|
rr1 := r1.lo&maskLow51Bits + c0
|
||||||
|
rr2 := r2.lo&maskLow51Bits + c1
|
||||||
|
rr3 := r3.lo&maskLow51Bits + c2
|
||||||
|
rr4 := r4.lo&maskLow51Bits + c3
|
||||||
|
|
||||||
|
// Now all coefficients fit into 64-bit registers but are still too large to
|
||||||
|
// be passed around as a Element. We therefore do one last carry chain,
|
||||||
|
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
||||||
|
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||||
|
v.carryPropagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func feSquareGeneric(v, a *Element) {
|
||||||
|
l0 := a.l0
|
||||||
|
l1 := a.l1
|
||||||
|
l2 := a.l2
|
||||||
|
l3 := a.l3
|
||||||
|
l4 := a.l4
|
||||||
|
|
||||||
|
// Squaring works precisely like multiplication above, but thanks to its
|
||||||
|
// symmetry we get to group a few terms together.
|
||||||
|
//
|
||||||
|
// l4 l3 l2 l1 l0 x
|
||||||
|
// l4 l3 l2 l1 l0 =
|
||||||
|
// ------------------------
|
||||||
|
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||||
|
// l4l1 l3l1 l2l1 l1l1 l0l1 +
|
||||||
|
// l4l2 l3l2 l2l2 l1l2 l0l2 +
|
||||||
|
// l4l3 l3l3 l2l3 l1l3 l0l3 +
|
||||||
|
// l4l4 l3l4 l2l4 l1l4 l0l4 =
|
||||||
|
// ----------------------------------------------
|
||||||
|
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||||
|
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
|
||||||
|
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
|
||||||
|
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
|
||||||
|
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
||||||
|
// --------------------------------------
|
||||||
|
// r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
|
||||||
|
// only three Mul64 and four Add64, instead of five and eight.
|
||||||
|
|
||||||
|
l0_2 := l0 * 2
|
||||||
|
l1_2 := l1 * 2
|
||||||
|
|
||||||
|
l1_38 := l1 * 38
|
||||||
|
l2_38 := l2 * 38
|
||||||
|
l3_38 := l3 * 38
|
||||||
|
|
||||||
|
l3_19 := l3 * 19
|
||||||
|
l4_19 := l4 * 19
|
||||||
|
|
||||||
|
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||||
|
r0 := mul64(l0, l0)
|
||||||
|
r0 = addMul64(r0, l1_38, l4)
|
||||||
|
r0 = addMul64(r0, l2_38, l3)
|
||||||
|
|
||||||
|
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||||
|
r1 := mul64(l0_2, l1)
|
||||||
|
r1 = addMul64(r1, l2_38, l4)
|
||||||
|
r1 = addMul64(r1, l3_19, l3)
|
||||||
|
|
||||||
|
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||||
|
r2 := mul64(l0_2, l2)
|
||||||
|
r2 = addMul64(r2, l1, l1)
|
||||||
|
r2 = addMul64(r2, l3_38, l4)
|
||||||
|
|
||||||
|
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||||
|
r3 := mul64(l0_2, l3)
|
||||||
|
r3 = addMul64(r3, l1_2, l2)
|
||||||
|
r3 = addMul64(r3, l4_19, l4)
|
||||||
|
|
||||||
|
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||||
|
r4 := mul64(l0_2, l4)
|
||||||
|
r4 = addMul64(r4, l1_2, l3)
|
||||||
|
r4 = addMul64(r4, l2, l2)
|
||||||
|
|
||||||
|
c0 := shiftRightBy51(r0)
|
||||||
|
c1 := shiftRightBy51(r1)
|
||||||
|
c2 := shiftRightBy51(r2)
|
||||||
|
c3 := shiftRightBy51(r3)
|
||||||
|
c4 := shiftRightBy51(r4)
|
||||||
|
|
||||||
|
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||||
|
rr1 := r1.lo&maskLow51Bits + c0
|
||||||
|
rr2 := r2.lo&maskLow51Bits + c1
|
||||||
|
rr3 := r3.lo&maskLow51Bits + c2
|
||||||
|
rr4 := r4.lo&maskLow51Bits + c3
|
||||||
|
|
||||||
|
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||||
|
v.carryPropagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// carryPropagate brings the limbs below 52 bits by applying the reduction
|
||||||
|
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
|
||||||
|
func (v *Element) carryPropagateGeneric() *Element {
|
||||||
|
c0 := v.l0 >> 51
|
||||||
|
c1 := v.l1 >> 51
|
||||||
|
c2 := v.l2 >> 51
|
||||||
|
c3 := v.l3 >> 51
|
||||||
|
c4 := v.l4 >> 51
|
||||||
|
|
||||||
|
v.l0 = v.l0&maskLow51Bits + c4*19
|
||||||
|
v.l1 = v.l1&maskLow51Bits + c0
|
||||||
|
v.l2 = v.l2&maskLow51Bits + c1
|
||||||
|
v.l3 = v.l3&maskLow51Bits + c2
|
||||||
|
v.l4 = v.l4&maskLow51Bits + c3
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
1027
vendor/filippo.io/edwards25519/scalar.go
generated
vendored
Normal file
1027
vendor/filippo.io/edwards25519/scalar.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
214
vendor/filippo.io/edwards25519/scalarmult.go
generated
vendored
Normal file
214
vendor/filippo.io/edwards25519/scalarmult.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// basepointTable is a set of 32 affineLookupTables, where table i is generated
|
||||||
|
// from 256i * basepoint. It is precomputed the first time it's used.
|
||||||
|
func basepointTable() *[32]affineLookupTable {
|
||||||
|
basepointTablePrecomp.initOnce.Do(func() {
|
||||||
|
p := NewGeneratorPoint()
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
basepointTablePrecomp.table[i].FromP3(p)
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
p.Add(p, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return &basepointTablePrecomp.table
|
||||||
|
}
|
||||||
|
|
||||||
|
var basepointTablePrecomp struct {
|
||||||
|
table [32]affineLookupTable
|
||||||
|
initOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarBaseMult sets v = x * B, where B is the canonical generator, and
|
||||||
|
// returns v.
|
||||||
|
//
|
||||||
|
// The scalar multiplication is done in constant time.
|
||||||
|
func (v *Point) ScalarBaseMult(x *Scalar) *Point {
|
||||||
|
basepointTable := basepointTable()
|
||||||
|
|
||||||
|
// Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i )
|
||||||
|
// as described in the Ed25519 paper
|
||||||
|
//
|
||||||
|
// Group even and odd coefficients
|
||||||
|
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||||
|
// + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B
|
||||||
|
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
|
||||||
|
// + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B)
|
||||||
|
//
|
||||||
|
// We use a lookup table for each i to get x_i*16^(2*i)*B
|
||||||
|
// and do four doublings to multiply by 16.
|
||||||
|
digits := x.signedRadix16()
|
||||||
|
|
||||||
|
multiple := &affineCached{}
|
||||||
|
tmp1 := &projP1xP1{}
|
||||||
|
tmp2 := &projP2{}
|
||||||
|
|
||||||
|
// Accumulate the odd components first
|
||||||
|
v.Set(NewIdentityPoint())
|
||||||
|
for i := 1; i < 64; i += 2 {
|
||||||
|
basepointTable[i/2].SelectInto(multiple, digits[i])
|
||||||
|
tmp1.AddAffine(v, multiple)
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply by 16
|
||||||
|
tmp2.FromP3(v) // tmp2 = v in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 2*v in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 2*v in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 4*v in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 4*v in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 8*v in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 8*v in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 16*v in P1xP1 coords
|
||||||
|
v.fromP1xP1(tmp1) // now v = 16*(odd components)
|
||||||
|
|
||||||
|
// Accumulate the even components
|
||||||
|
for i := 0; i < 64; i += 2 {
|
||||||
|
basepointTable[i/2].SelectInto(multiple, digits[i])
|
||||||
|
tmp1.AddAffine(v, multiple)
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarMult sets v = x * q, and returns v.
|
||||||
|
//
|
||||||
|
// The scalar multiplication is done in constant time.
|
||||||
|
func (v *Point) ScalarMult(x *Scalar, q *Point) *Point {
|
||||||
|
checkInitialized(q)
|
||||||
|
|
||||||
|
var table projLookupTable
|
||||||
|
table.FromP3(q)
|
||||||
|
|
||||||
|
// Write x = sum(x_i * 16^i)
|
||||||
|
// so x*Q = sum( Q*x_i*16^i )
|
||||||
|
// = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... )
|
||||||
|
// <------compute inside out---------
|
||||||
|
//
|
||||||
|
// We use the lookup table to get the x_i*Q values
|
||||||
|
// and do four doublings to compute 16*Q
|
||||||
|
digits := x.signedRadix16()
|
||||||
|
|
||||||
|
// Unwrap first loop iteration to save computing 16*identity
|
||||||
|
multiple := &projCached{}
|
||||||
|
tmp1 := &projP1xP1{}
|
||||||
|
tmp2 := &projP2{}
|
||||||
|
table.SelectInto(multiple, digits[63])
|
||||||
|
|
||||||
|
v.Set(NewIdentityPoint())
|
||||||
|
tmp1.Add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords
|
||||||
|
for i := 62; i >= 0; i-- {
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = (prev) in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
|
||||||
|
tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
|
||||||
|
tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
|
||||||
|
v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
|
||||||
|
table.SelectInto(multiple, digits[i])
|
||||||
|
tmp1.Add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords
|
||||||
|
}
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// basepointNafTable is the nafLookupTable8 for the basepoint.
|
||||||
|
// It is precomputed the first time it's used.
|
||||||
|
func basepointNafTable() *nafLookupTable8 {
|
||||||
|
basepointNafTablePrecomp.initOnce.Do(func() {
|
||||||
|
basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint())
|
||||||
|
})
|
||||||
|
return &basepointNafTablePrecomp.table
|
||||||
|
}
|
||||||
|
|
||||||
|
var basepointNafTablePrecomp struct {
|
||||||
|
table nafLookupTable8
|
||||||
|
initOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarTimeDoubleScalarBaseMult sets v = a * A + b * B, where B is the canonical
|
||||||
|
// generator, and returns v.
|
||||||
|
//
|
||||||
|
// Execution time depends on the inputs.
|
||||||
|
func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point {
|
||||||
|
checkInitialized(A)
|
||||||
|
|
||||||
|
// Similarly to the single variable-base approach, we compute
|
||||||
|
// digits and use them with a lookup table. However, because
|
||||||
|
// we are allowed to do variable-time operations, we don't
|
||||||
|
// need constant-time lookups or constant-time digit
|
||||||
|
// computations.
|
||||||
|
//
|
||||||
|
// So we use a non-adjacent form of some width w instead of
|
||||||
|
// radix 16. This is like a binary representation (one digit
|
||||||
|
// for each binary place) but we allow the digits to grow in
|
||||||
|
// magnitude up to 2^{w-1} so that the nonzero digits are as
|
||||||
|
// sparse as possible. Intuitively, this "condenses" the
|
||||||
|
// "mass" of the scalar onto sparse coefficients (meaning
|
||||||
|
// fewer additions).
|
||||||
|
|
||||||
|
basepointNafTable := basepointNafTable()
|
||||||
|
var aTable nafLookupTable5
|
||||||
|
aTable.FromP3(A)
|
||||||
|
// Because the basepoint is fixed, we can use a wider NAF
|
||||||
|
// corresponding to a bigger table.
|
||||||
|
aNaf := a.nonAdjacentForm(5)
|
||||||
|
bNaf := b.nonAdjacentForm(8)
|
||||||
|
|
||||||
|
// Find the first nonzero coefficient.
|
||||||
|
i := 255
|
||||||
|
for j := i; j >= 0; j-- {
|
||||||
|
if aNaf[j] != 0 || bNaf[j] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multA := &projCached{}
|
||||||
|
multB := &affineCached{}
|
||||||
|
tmp1 := &projP1xP1{}
|
||||||
|
tmp2 := &projP2{}
|
||||||
|
tmp2.Zero()
|
||||||
|
|
||||||
|
// Move from high to low bits, doubling the accumulator
|
||||||
|
// at each iteration and checking whether there is a nonzero
|
||||||
|
// coefficient to look up a multiple of.
|
||||||
|
for ; i >= 0; i-- {
|
||||||
|
tmp1.Double(tmp2)
|
||||||
|
|
||||||
|
// Only update v if we have a nonzero coeff to add in.
|
||||||
|
if aNaf[i] > 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
aTable.SelectInto(multA, aNaf[i])
|
||||||
|
tmp1.Add(v, multA)
|
||||||
|
} else if aNaf[i] < 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
aTable.SelectInto(multA, -aNaf[i])
|
||||||
|
tmp1.Sub(v, multA)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bNaf[i] > 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
basepointNafTable.SelectInto(multB, bNaf[i])
|
||||||
|
tmp1.AddAffine(v, multB)
|
||||||
|
} else if bNaf[i] < 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
basepointNafTable.SelectInto(multB, -bNaf[i])
|
||||||
|
tmp1.SubAffine(v, multB)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp2.FromP1xP1(tmp1)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.fromP2(tmp2)
|
||||||
|
return v
|
||||||
|
}
|
129
vendor/filippo.io/edwards25519/tables.go
generated
vendored
Normal file
129
vendor/filippo.io/edwards25519/tables.go
generated
vendored
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A dynamic lookup table for variable-base, constant-time scalar muls.
|
||||||
|
type projLookupTable struct {
|
||||||
|
points [8]projCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// A precomputed lookup table for fixed-base, constant-time scalar muls.
|
||||||
|
type affineLookupTable struct {
|
||||||
|
points [8]affineCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// A dynamic lookup table for variable-base, variable-time scalar muls.
|
||||||
|
type nafLookupTable5 struct {
|
||||||
|
points [8]projCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// A precomputed lookup table for fixed-base, variable-time scalar muls.
|
||||||
|
type nafLookupTable8 struct {
|
||||||
|
points [64]affineCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructors.
|
||||||
|
|
||||||
|
// Builds a lookup table at runtime. Fast.
|
||||||
|
func (v *projLookupTable) FromP3(q *Point) {
|
||||||
|
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||||
|
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
tmpP3 := Point{}
|
||||||
|
tmpP1xP1 := projP1xP1{}
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
|
||||||
|
// This is needlessly complicated because the API has explicit
|
||||||
|
// recievers instead of creating stack objects and relying on RVO
|
||||||
|
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||||
|
func (v *affineLookupTable) FromP3(q *Point) {
|
||||||
|
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||||
|
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
tmpP3 := Point{}
|
||||||
|
tmpP1xP1 := projP1xP1{}
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
|
||||||
|
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a lookup table at runtime. Fast.
|
||||||
|
func (v *nafLookupTable5) FromP3(q *Point) {
|
||||||
|
// Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q
|
||||||
|
// This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
q2 := Point{}
|
||||||
|
q2.Add(q, q)
|
||||||
|
tmpP3 := Point{}
|
||||||
|
tmpP1xP1 := projP1xP1{}
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(&q2, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not optimised for speed; fixed-base tables should be precomputed.
|
||||||
|
func (v *nafLookupTable8) FromP3(q *Point) {
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
q2 := Point{}
|
||||||
|
q2.Add(q, q)
|
||||||
|
tmpP3 := Point{}
|
||||||
|
tmpP1xP1 := projP1xP1{}
|
||||||
|
for i := 0; i < 63; i++ {
|
||||||
|
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selectors.
|
||||||
|
|
||||||
|
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||||
|
func (v *projLookupTable) SelectInto(dest *projCached, x int8) {
|
||||||
|
// Compute xabs = |x|
|
||||||
|
xmask := x >> 7
|
||||||
|
xabs := uint8((x + xmask) ^ xmask)
|
||||||
|
|
||||||
|
dest.Zero()
|
||||||
|
for j := 1; j <= 8; j++ {
|
||||||
|
// Set dest = j*Q if |x| = j
|
||||||
|
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||||
|
dest.Select(&v.points[j-1], dest, cond)
|
||||||
|
}
|
||||||
|
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||||
|
dest.CondNeg(int(xmask & 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||||
|
func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) {
|
||||||
|
// Compute xabs = |x|
|
||||||
|
xmask := x >> 7
|
||||||
|
xabs := uint8((x + xmask) ^ xmask)
|
||||||
|
|
||||||
|
dest.Zero()
|
||||||
|
for j := 1; j <= 8; j++ {
|
||||||
|
// Set dest = j*Q if |x| = j
|
||||||
|
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||||
|
dest.Select(&v.points[j-1], dest, cond)
|
||||||
|
}
|
||||||
|
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||||
|
dest.CondNeg(int(xmask & 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given odd x with 0 < x < 2^4, return x*Q (in variable time).
|
||||||
|
func (v *nafLookupTable5) SelectInto(dest *projCached, x int8) {
|
||||||
|
*dest = v.points[x/2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given odd x with 0 < x < 2^7, return x*Q (in variable time).
|
||||||
|
func (v *nafLookupTable8) SelectInto(dest *affineCached, x int8) {
|
||||||
|
*dest = v.points[x/2]
|
||||||
|
}
|
24
vendor/github.com/Baozisoftware/qrcode-terminal-go/.gitignore
generated
vendored
24
vendor/github.com/Baozisoftware/qrcode-terminal-go/.gitignore
generated
vendored
@ -1,24 +0,0 @@
|
|||||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Folders
|
|
||||||
_obj
|
|
||||||
_test
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
|
||||||
*.[568vq]
|
|
||||||
[568vq].out
|
|
||||||
|
|
||||||
*.cgo1.go
|
|
||||||
*.cgo2.c
|
|
||||||
_cgo_defun.c
|
|
||||||
_cgo_gotypes.go
|
|
||||||
_cgo_export.*
|
|
||||||
|
|
||||||
_testmain.go
|
|
||||||
|
|
||||||
*.exe
|
|
||||||
*.test
|
|
||||||
*.prof
|
|
29
vendor/github.com/Baozisoftware/qrcode-terminal-go/LICENSE
generated
vendored
29
vendor/github.com/Baozisoftware/qrcode-terminal-go/LICENSE
generated
vendored
@ -1,29 +0,0 @@
|
|||||||
BSD 3-Clause License
|
|
||||||
|
|
||||||
Copyright (c) 2017, Baozisoftware
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of the copyright holder nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
vendor/github.com/Baozisoftware/qrcode-terminal-go/README.md
generated
vendored
39
vendor/github.com/Baozisoftware/qrcode-terminal-go/README.md
generated
vendored
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
# qrcode-terminal-go
|
|
||||||
QRCode terminal for golang.
|
|
||||||
|
|
||||||
# Example
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import "github.com/Baozisoftware/qrcode-terminal-go"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
Test1()
|
|
||||||
Test2()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test1(){
|
|
||||||
content := "Hello, 世界"
|
|
||||||
obj := qrcodeTerminal.New()
|
|
||||||
obj.Get(content).Print()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test2(){
|
|
||||||
content := "https://github.com/Baozisoftware/qrcode-terminal-go"
|
|
||||||
obj := qrcodeTerminal.New2(qrcodeTerminal.ConsoleColors.BrightBlue,qrcodeTerminal.ConsoleColors.BrightGreen,qrcodeTerminal.QRCodeRecoveryLevels.Low)
|
|
||||||
obj.Get([]byte(content)).Print()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Screenshots
|
|
||||||
### Windows XP
|
|
||||||
![winxp](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/winxp.png)
|
|
||||||
### Windows 7
|
|
||||||
![win7](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/win7.png)
|
|
||||||
### Windows 10
|
|
||||||
![win10](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/win10.png)
|
|
||||||
### Ubuntu
|
|
||||||
![ubuntu](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/ubuntu.png)
|
|
||||||
### macOS
|
|
||||||
![macos](https://github.com/Baozisoftware/qrcode-terminal-go/blob/master/screenshots/macos.png)
|
|
155
vendor/github.com/Baozisoftware/qrcode-terminal-go/qrcodeTerminal.go
generated
vendored
155
vendor/github.com/Baozisoftware/qrcode-terminal-go/qrcodeTerminal.go
generated
vendored
@ -1,155 +0,0 @@
|
|||||||
package qrcodeTerminal
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/skip2/go-qrcode"
|
|
||||||
"github.com/mattn/go-colorable"
|
|
||||||
"image/png"
|
|
||||||
nbytes "bytes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type consoleColor string
|
|
||||||
type consoleColors struct {
|
|
||||||
NormalBlack consoleColor
|
|
||||||
NormalRed consoleColor
|
|
||||||
NormalGreen consoleColor
|
|
||||||
NormalYellow consoleColor
|
|
||||||
NormalBlue consoleColor
|
|
||||||
NormalMagenta consoleColor
|
|
||||||
NormalCyan consoleColor
|
|
||||||
NormalWhite consoleColor
|
|
||||||
BrightBlack consoleColor
|
|
||||||
BrightRed consoleColor
|
|
||||||
BrightGreen consoleColor
|
|
||||||
BrightYellow consoleColor
|
|
||||||
BrightBlue consoleColor
|
|
||||||
BrightMagenta consoleColor
|
|
||||||
BrightCyan consoleColor
|
|
||||||
BrightWhite consoleColor
|
|
||||||
}
|
|
||||||
type qrcodeRecoveryLevel qrcode.RecoveryLevel
|
|
||||||
type qrcodeRecoveryLevels struct {
|
|
||||||
Low qrcodeRecoveryLevel
|
|
||||||
Medium qrcodeRecoveryLevel
|
|
||||||
High qrcodeRecoveryLevel
|
|
||||||
Highest qrcodeRecoveryLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ConsoleColors consoleColors = consoleColors{
|
|
||||||
NormalBlack: "\033[38;5;0m \033[0m",
|
|
||||||
NormalRed: "\033[38;5;1m \033[0m",
|
|
||||||
NormalGreen: "\033[38;5;2m \033[0m",
|
|
||||||
NormalYellow: "\033[38;5;3m \033[0m",
|
|
||||||
NormalBlue: "\033[38;5;4m \033[0m",
|
|
||||||
NormalMagenta: "\033[38;5;5m \033[0m",
|
|
||||||
NormalCyan: "\033[38;5;6m \033[0m",
|
|
||||||
NormalWhite: "\033[38;5;7m \033[0m",
|
|
||||||
BrightBlack: "\033[48;5;0m \033[0m",
|
|
||||||
BrightRed: "\033[48;5;1m \033[0m",
|
|
||||||
BrightGreen: "\033[48;5;2m \033[0m",
|
|
||||||
BrightYellow: "\033[48;5;3m \033[0m",
|
|
||||||
BrightBlue: "\033[48;5;4m \033[0m",
|
|
||||||
BrightMagenta: "\033[48;5;5m \033[0m",
|
|
||||||
BrightCyan: "\033[48;5;6m \033[0m",
|
|
||||||
BrightWhite: "\033[48;5;7m \033[0m"}
|
|
||||||
QRCodeRecoveryLevels = qrcodeRecoveryLevels{
|
|
||||||
Low: qrcodeRecoveryLevel(qrcode.Low),
|
|
||||||
Medium: qrcodeRecoveryLevel(qrcode.Medium),
|
|
||||||
High: qrcodeRecoveryLevel(qrcode.High),
|
|
||||||
Highest: qrcodeRecoveryLevel(qrcode.Highest)}
|
|
||||||
)
|
|
||||||
|
|
||||||
type QRCodeString string
|
|
||||||
|
|
||||||
func (v *QRCodeString) Print() {
|
|
||||||
fmt.Fprint(outer, *v)
|
|
||||||
}
|
|
||||||
|
|
||||||
type qrcodeTerminal struct {
|
|
||||||
front consoleColor
|
|
||||||
back consoleColor
|
|
||||||
level qrcodeRecoveryLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *qrcodeTerminal) Get(content interface{}) (result *QRCodeString) {
|
|
||||||
var qr *qrcode.QRCode
|
|
||||||
var err error
|
|
||||||
if t, ok := content.(string); ok {
|
|
||||||
qr, err = qrcode.New(t, qrcode.RecoveryLevel(v.level))
|
|
||||||
} else if t, ok := content.([]byte); ok {
|
|
||||||
qr, err = qrcode.New(string(t), qrcode.RecoveryLevel(v.level))
|
|
||||||
}
|
|
||||||
if qr != nil && err == nil {
|
|
||||||
data := qr.Bitmap()
|
|
||||||
result = v.getQRCodeString(data)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *qrcodeTerminal) Get2(bytes []byte) (result *QRCodeString) {
|
|
||||||
data, err := parseQR(bytes)
|
|
||||||
if err == nil {
|
|
||||||
result = v.getQRCodeString(data)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func New2(front, back consoleColor, level qrcodeRecoveryLevel) *qrcodeTerminal {
|
|
||||||
obj := qrcodeTerminal{front: front, back: back, level: level}
|
|
||||||
return &obj
|
|
||||||
}
|
|
||||||
|
|
||||||
func New() *qrcodeTerminal {
|
|
||||||
front, back, level := ConsoleColors.BrightBlack, ConsoleColors.BrightWhite, QRCodeRecoveryLevels.Medium
|
|
||||||
return New2(front, back, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *qrcodeTerminal) getQRCodeString(data [][]bool) (result *QRCodeString) {
|
|
||||||
str := ""
|
|
||||||
for ir, row := range data {
|
|
||||||
lr := len(row)
|
|
||||||
if ir == 0 || ir == 1 || ir == 2 ||
|
|
||||||
ir == lr-1 || ir == lr-2 || ir == lr-3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for ic, col := range row {
|
|
||||||
lc := len(data)
|
|
||||||
if ic == 0 || ic == 1 || ic == 2 ||
|
|
||||||
ic == lc-1 || ic == lc-2 || ic == lc-3 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if col {
|
|
||||||
str += fmt.Sprint(v.front)
|
|
||||||
} else {
|
|
||||||
str += fmt.Sprint(v.back)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
str += fmt.Sprintln()
|
|
||||||
}
|
|
||||||
obj := QRCodeString(str)
|
|
||||||
result = &obj
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseQR(bytes []byte) (data [][]bool, err error) {
|
|
||||||
r := nbytes.NewReader(bytes)
|
|
||||||
img, err := png.Decode(r)
|
|
||||||
if err == nil {
|
|
||||||
rect := img.Bounds()
|
|
||||||
mx, my := rect.Max.X, rect.Max.Y
|
|
||||||
data = make([][]bool, mx)
|
|
||||||
for x := 0; x < mx; x++ {
|
|
||||||
data[x] = make([]bool, my)
|
|
||||||
for y := 0; y < my; y++ {
|
|
||||||
c := img.At(x, y)
|
|
||||||
r, _, _, _ := c.RGBA()
|
|
||||||
data[x][y] = r == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var outer = colorable.NewColorableStdout()
|
|
3
vendor/github.com/Rhymen/go-whatsapp/.gitignore
generated
vendored
3
vendor/github.com/Rhymen/go-whatsapp/.gitignore
generated
vendored
@ -1,3 +0,0 @@
|
|||||||
.idea/
|
|
||||||
docs/
|
|
||||||
build/
|
|
21
vendor/github.com/Rhymen/go-whatsapp/LICENSE
generated
vendored
21
vendor/github.com/Rhymen/go-whatsapp/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2018
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
139
vendor/github.com/Rhymen/go-whatsapp/README.md
generated
vendored
139
vendor/github.com/Rhymen/go-whatsapp/README.md
generated
vendored
@ -1,139 +0,0 @@
|
|||||||
# go-whatsapp
|
|
||||||
Package rhymen/go-whatsapp implements the WhatsApp Web API to provide a clean interface for developers. Big thanks to all contributors of the [sigalor/whatsapp-web-reveng](https://github.com/sigalor/whatsapp-web-reveng) project. The official WhatsApp Business API was released in August 2018. You can check it out [here](https://www.whatsapp.com/business/api).
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
```sh
|
|
||||||
go get github.com/Rhymen/go-whatsapp
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
### Creating a connection
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
whatsapp "github.com/Rhymen/go-whatsapp"
|
|
||||||
)
|
|
||||||
|
|
||||||
wac, err := whatsapp.NewConn(20 * time.Second)
|
|
||||||
```
|
|
||||||
The duration passed to the NewConn function is used to timeout login requests. If you have a bad internet connection use a higher timeout value. This function only creates a websocket connection, it does not handle authentication.
|
|
||||||
|
|
||||||
### Login
|
|
||||||
```go
|
|
||||||
qrChan := make(chan string)
|
|
||||||
go func() {
|
|
||||||
fmt.Printf("qr code: %v\n", <-qrChan)
|
|
||||||
//show qr code or save it somewhere to scan
|
|
||||||
}()
|
|
||||||
sess, err := wac.Login(qrChan)
|
|
||||||
```
|
|
||||||
The authentication process requires you to scan the qr code, that is send through the channel, with the device you are using whatsapp on. The session struct that is returned can be saved and used to restore the login without scanning the qr code again. The qr code has a ttl of 20 seconds and the login function throws a timeout err if the time has passed or any other request fails.
|
|
||||||
|
|
||||||
### Restore
|
|
||||||
```go
|
|
||||||
newSess, err := wac.RestoreWithSession(sess)
|
|
||||||
```
|
|
||||||
The restore function needs a valid session and returns the new session that was created.
|
|
||||||
|
|
||||||
### Add message handlers
|
|
||||||
```go
|
|
||||||
type myHandler struct{}
|
|
||||||
|
|
||||||
func (myHandler) HandleError(err error) {
|
|
||||||
fmt.Fprintf(os.Stderr, "%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleTextMessage(message whatsapp.TextMessage) {
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleImageMessage(message whatsapp.ImageMessage) {
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleDocumentMessage(message whatsapp.DocumentMessage) {
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleVideoMessage(message whatsapp.VideoMessage) {
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleAudioMessage(message whatsapp.AudioMessage){
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleJsonMessage(message string) {
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleContactMessage(message whatsapp.ContactMessage) {
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleBatteryMessage(message whatsapp.BatteryMessage) {
|
|
||||||
fmt.Println(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (myHandler) HandleNewContact(contact whatsapp.Contact) {
|
|
||||||
fmt.Println(contact)
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.AddHandler(myHandler{})
|
|
||||||
```
|
|
||||||
The message handlers are all optional, you don't need to implement anything but the error handler to implement the interface. The ImageMessage, VideoMessage, AudioMessage and DocumentMessage provide a Download function to get the media data.
|
|
||||||
|
|
||||||
### Sending text messages
|
|
||||||
```go
|
|
||||||
text := whatsapp.TextMessage{
|
|
||||||
Info: whatsapp.MessageInfo{
|
|
||||||
RemoteJid: "0123456789@s.whatsapp.net",
|
|
||||||
},
|
|
||||||
Text: "Hello Whatsapp",
|
|
||||||
}
|
|
||||||
|
|
||||||
err := wac.Send(text)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Sending Contact Messages
|
|
||||||
```go
|
|
||||||
contactMessage := whatsapp.ContactMessage{
|
|
||||||
Info: whatsapp.MessageInfo{
|
|
||||||
RemoteJid: "0123456789@s.whatsapp.net",
|
|
||||||
},
|
|
||||||
DisplayName: "Luke Skylwallker",
|
|
||||||
Vcard: "BEGIN:VCARD\nVERSION:3.0\nN:Skyllwalker;Luke;;\nFN:Luke Skywallker\nitem1.TEL;waid=0123456789:+1 23 456789789\nitem1.X-ABLabel:Mobile\nEND:VCARD",
|
|
||||||
}
|
|
||||||
|
|
||||||
id, error := client.WaConn.Send(contactMessage)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
The message will be send over the websocket. The attributes seen above are the required ones. All other relevant attributes (id, timestamp, fromMe, status) are set if they are missing in the struct. For the time being we only support text messages, but other types are planned for the near future.
|
|
||||||
|
|
||||||
## Legal
|
|
||||||
This code is in no way affiliated with, authorized, maintained, sponsored or endorsed by WhatsApp or any of its
|
|
||||||
affiliates or subsidiaries. This is an independent and unofficial software. Use at your own risk.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2018
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
388
vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go
generated
vendored
388
vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go
generated
vendored
@ -1,388 +0,0 @@
|
|||||||
package binary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary/token"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type binaryDecoder struct {
|
|
||||||
data []byte
|
|
||||||
index int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDecoder(data []byte) *binaryDecoder {
|
|
||||||
return &binaryDecoder{data, 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) checkEOS(length int) error {
|
|
||||||
if r.index+length > len(r.data) {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readByte() (byte, error) {
|
|
||||||
if err := r.checkEOS(1); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
b := r.data[r.index]
|
|
||||||
r.index++
|
|
||||||
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readIntN(n int, littleEndian bool) (int, error) {
|
|
||||||
if err := r.checkEOS(n); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret int
|
|
||||||
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
var curShift int
|
|
||||||
if littleEndian {
|
|
||||||
curShift = i
|
|
||||||
} else {
|
|
||||||
curShift = n - i - 1
|
|
||||||
}
|
|
||||||
ret |= int(r.data[r.index+i]) << uint(curShift*8)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.index += n
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readInt8(littleEndian bool) (int, error) {
|
|
||||||
return r.readIntN(1, littleEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readInt16(littleEndian bool) (int, error) {
|
|
||||||
return r.readIntN(2, littleEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readInt20() (int, error) {
|
|
||||||
if err := r.checkEOS(3); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := ((int(r.data[r.index]) & 15) << 16) + (int(r.data[r.index+1]) << 8) + int(r.data[r.index+2])
|
|
||||||
r.index += 3
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readInt32(littleEndian bool) (int, error) {
|
|
||||||
return r.readIntN(4, littleEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readInt64(littleEndian bool) (int, error) {
|
|
||||||
return r.readIntN(8, littleEndian)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readPacked8(tag int) (string, error) {
|
|
||||||
startByte, err := r.readByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := ""
|
|
||||||
|
|
||||||
for i := 0; i < int(startByte&127); i++ {
|
|
||||||
currByte, err := r.readByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
lower, err := unpackByte(tag, currByte&0xF0>>4)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
upper, err := unpackByte(tag, currByte&0x0F)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret += lower + upper
|
|
||||||
}
|
|
||||||
|
|
||||||
if startByte>>7 != 0 {
|
|
||||||
ret = ret[:len(ret)-1]
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackByte(tag int, value byte) (string, error) {
|
|
||||||
switch tag {
|
|
||||||
case token.NIBBLE_8:
|
|
||||||
return unpackNibble(value)
|
|
||||||
case token.HEX_8:
|
|
||||||
return unpackHex(value)
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("unpackByte with unknown tag %d", tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackNibble(value byte) (string, error) {
|
|
||||||
switch {
|
|
||||||
case value < 0 || value > 15:
|
|
||||||
return "", fmt.Errorf("unpackNibble with value %d", value)
|
|
||||||
case value == 10:
|
|
||||||
return "-", nil
|
|
||||||
case value == 11:
|
|
||||||
return ".", nil
|
|
||||||
case value == 15:
|
|
||||||
return "\x00", nil
|
|
||||||
default:
|
|
||||||
return strconv.Itoa(int(value)), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func unpackHex(value byte) (string, error) {
|
|
||||||
switch {
|
|
||||||
case value < 0 || value > 15:
|
|
||||||
return "", fmt.Errorf("unpackHex with value %d", value)
|
|
||||||
case value < 10:
|
|
||||||
return strconv.Itoa(int(value)), nil
|
|
||||||
default:
|
|
||||||
return string('A' + value - 10), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readListSize(tag int) (int, error) {
|
|
||||||
switch tag {
|
|
||||||
case token.LIST_EMPTY:
|
|
||||||
return 0, nil
|
|
||||||
case token.LIST_8:
|
|
||||||
return r.readInt8(false)
|
|
||||||
case token.LIST_16:
|
|
||||||
return r.readInt16(false)
|
|
||||||
default:
|
|
||||||
return 0, fmt.Errorf("readListSize with unknown tag %d at position %d", tag, r.index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readString(tag int) (string, error) {
|
|
||||||
switch {
|
|
||||||
case tag >= 3 && tag <= len(token.SingleByteTokens):
|
|
||||||
tok, err := token.GetSingleToken(tag)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if tok == "s.whatsapp.net" {
|
|
||||||
tok = "c.us"
|
|
||||||
}
|
|
||||||
|
|
||||||
return tok, nil
|
|
||||||
case tag == token.DICTIONARY_0 || tag == token.DICTIONARY_1 || tag == token.DICTIONARY_2 || tag == token.DICTIONARY_3:
|
|
||||||
i, err := r.readInt8(false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return token.GetDoubleToken(tag-token.DICTIONARY_0, i)
|
|
||||||
case tag == token.LIST_EMPTY:
|
|
||||||
return "", nil
|
|
||||||
case tag == token.BINARY_8:
|
|
||||||
length, err := r.readInt8(false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.readStringFromChars(length)
|
|
||||||
case tag == token.BINARY_20:
|
|
||||||
length, err := r.readInt20()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.readStringFromChars(length)
|
|
||||||
case tag == token.BINARY_32:
|
|
||||||
length, err := r.readInt32(false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.readStringFromChars(length)
|
|
||||||
case tag == token.JID_PAIR:
|
|
||||||
b, err := r.readByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
i, err := r.readString(int(b))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err = r.readByte()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
j, err := r.readString(int(b))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if i == "" || j == "" {
|
|
||||||
return "", fmt.Errorf("invalid jid pair: %s - %s", i, j)
|
|
||||||
}
|
|
||||||
|
|
||||||
return i + "@" + j, nil
|
|
||||||
case tag == token.NIBBLE_8 || tag == token.HEX_8:
|
|
||||||
return r.readPacked8(tag)
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("invalid string with tag %d", tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readStringFromChars(length int) (string, error) {
|
|
||||||
if err := r.checkEOS(length); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := r.data[r.index : r.index+length]
|
|
||||||
r.index += length
|
|
||||||
|
|
||||||
return string(ret), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readAttributes(n int) (map[string]string, error) {
|
|
||||||
if n == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := make(map[string]string)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
idx, err := r.readInt8(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
index, err := r.readString(idx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
idx, err = r.readInt8(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret[index], err = r.readString(idx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readList(tag int) ([]Node, error) {
|
|
||||||
size, err := r.readListSize(tag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := make([]Node, size)
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
n, err := r.ReadNode()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret[i] = *n
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) ReadNode() (*Node, error) {
|
|
||||||
ret := &Node{}
|
|
||||||
|
|
||||||
size, err := r.readInt8(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
listSize, err := r.readListSize(size)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
descrTag, err := r.readInt8(false)
|
|
||||||
if descrTag == token.STREAM_END {
|
|
||||||
return nil, fmt.Errorf("unexpected stream end")
|
|
||||||
}
|
|
||||||
ret.Description, err = r.readString(descrTag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if listSize == 0 || ret.Description == "" {
|
|
||||||
return nil, fmt.Errorf("invalid Node")
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Attributes, err = r.readAttributes((listSize - 1) >> 1)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if listSize%2 == 1 {
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tag, err := r.readInt8(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch tag {
|
|
||||||
case token.LIST_EMPTY, token.LIST_8, token.LIST_16:
|
|
||||||
ret.Content, err = r.readList(tag)
|
|
||||||
case token.BINARY_8:
|
|
||||||
size, err = r.readInt8(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Content, err = r.readBytes(size)
|
|
||||||
case token.BINARY_20:
|
|
||||||
size, err = r.readInt20()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Content, err = r.readBytes(size)
|
|
||||||
case token.BINARY_32:
|
|
||||||
size, err = r.readInt32(false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.Content, err = r.readBytes(size)
|
|
||||||
default:
|
|
||||||
ret.Content, err = r.readString(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *binaryDecoder) readBytes(n int) ([]byte, error) {
|
|
||||||
ret := make([]byte, n)
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for i := range ret {
|
|
||||||
ret[i], err = r.readByte()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
351
vendor/github.com/Rhymen/go-whatsapp/binary/encoder.go
generated
vendored
351
vendor/github.com/Rhymen/go-whatsapp/binary/encoder.go
generated
vendored
@ -1,351 +0,0 @@
|
|||||||
package binary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary/token"
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type binaryEncoder struct {
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEncoder() *binaryEncoder {
|
|
||||||
return &binaryEncoder{make([]byte, 0)}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) GetData() []byte {
|
|
||||||
return w.data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushByte(b byte) {
|
|
||||||
w.data = append(w.data, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushBytes(bytes []byte) {
|
|
||||||
w.data = append(w.data, bytes...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushIntN(value, n int, littleEndian bool) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
var curShift int
|
|
||||||
if littleEndian {
|
|
||||||
curShift = i
|
|
||||||
} else {
|
|
||||||
curShift = n - i - 1
|
|
||||||
}
|
|
||||||
w.pushByte(byte((value >> uint(curShift*8)) & 0xFF))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushInt20(value int) {
|
|
||||||
w.pushBytes([]byte{byte((value >> 16) & 0x0F), byte((value >> 8) & 0xFF), byte(value & 0xFF)})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushInt8(value int) {
|
|
||||||
w.pushIntN(value, 1, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushInt16(value int) {
|
|
||||||
w.pushIntN(value, 2, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushInt32(value int) {
|
|
||||||
w.pushIntN(value, 4, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushInt64(value int) {
|
|
||||||
w.pushIntN(value, 8, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) pushString(value string) {
|
|
||||||
w.pushBytes([]byte(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeByteLength(length int) error {
|
|
||||||
if length > math.MaxInt32 {
|
|
||||||
return fmt.Errorf("length is too large: %d", length)
|
|
||||||
} else if length >= (1 << 20) {
|
|
||||||
w.pushByte(token.BINARY_32)
|
|
||||||
w.pushInt32(length)
|
|
||||||
} else if length >= 256 {
|
|
||||||
w.pushByte(token.BINARY_20)
|
|
||||||
w.pushInt20(length)
|
|
||||||
} else {
|
|
||||||
w.pushByte(token.BINARY_8)
|
|
||||||
w.pushInt8(length)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) WriteNode(n Node) error {
|
|
||||||
numAttributes := 0
|
|
||||||
if n.Attributes != nil {
|
|
||||||
numAttributes = len(n.Attributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
hasContent := 0
|
|
||||||
if n.Content != nil {
|
|
||||||
hasContent = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
w.writeListStart(2*numAttributes + 1 + hasContent)
|
|
||||||
if err := w.writeString(n.Description, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.writeAttributes(n.Attributes); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.writeChildren(n.Content); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeString(tok string, i bool) error {
|
|
||||||
if !i && tok == "c.us" {
|
|
||||||
if err := w.writeToken(token.IndexOfSingleToken("s.whatsapp.net")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenIndex := token.IndexOfSingleToken(tok)
|
|
||||||
if tokenIndex == -1 {
|
|
||||||
jidSepIndex := strings.Index(tok, "@")
|
|
||||||
if jidSepIndex < 1 {
|
|
||||||
w.writeStringRaw(tok)
|
|
||||||
} else {
|
|
||||||
w.writeJid(tok[:jidSepIndex], tok[jidSepIndex+1:])
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if tokenIndex < token.SINGLE_BYTE_MAX {
|
|
||||||
if err := w.writeToken(tokenIndex); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
singleByteOverflow := tokenIndex - token.SINGLE_BYTE_MAX
|
|
||||||
dictionaryIndex := singleByteOverflow >> 8
|
|
||||||
if dictionaryIndex < 0 || dictionaryIndex > 3 {
|
|
||||||
return fmt.Errorf("double byte dictionary token out of range: %v", tok)
|
|
||||||
}
|
|
||||||
if err := w.writeToken(token.DICTIONARY_0 + dictionaryIndex); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := w.writeToken(singleByteOverflow % 256); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeStringRaw(value string) error {
|
|
||||||
if err := w.writeByteLength(len(value)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.pushString(value)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeJid(jidLeft, jidRight string) error {
|
|
||||||
w.pushByte(token.JID_PAIR)
|
|
||||||
|
|
||||||
if jidLeft != "" {
|
|
||||||
if err := w.writePackedBytes(jidLeft); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := w.writeToken(token.LIST_EMPTY); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.writeString(jidRight, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeToken(tok int) error {
|
|
||||||
if tok < len(token.SingleByteTokens) {
|
|
||||||
w.pushByte(byte(tok))
|
|
||||||
} else if tok <= 500 {
|
|
||||||
return fmt.Errorf("invalid token: %d", tok)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeAttributes(attributes map[string]string) error {
|
|
||||||
if attributes == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, val := range attributes {
|
|
||||||
if val == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.writeString(key, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := w.writeString(val, false); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeChildren(children interface{}) error {
|
|
||||||
if children == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
switch childs := children.(type) {
|
|
||||||
case string:
|
|
||||||
if err := w.writeString(childs, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case []byte:
|
|
||||||
if err := w.writeByteLength(len(childs)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.pushBytes(childs)
|
|
||||||
case []Node:
|
|
||||||
w.writeListStart(len(childs))
|
|
||||||
for _, n := range childs {
|
|
||||||
if err := w.WriteNode(n); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("cannot write child of type: %T", children)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writeListStart(listSize int) {
|
|
||||||
if listSize == 0 {
|
|
||||||
w.pushByte(byte(token.LIST_EMPTY))
|
|
||||||
} else if listSize < 256 {
|
|
||||||
w.pushByte(byte(token.LIST_8))
|
|
||||||
w.pushInt8(listSize)
|
|
||||||
} else {
|
|
||||||
w.pushByte(byte(token.LIST_16))
|
|
||||||
w.pushInt16(listSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writePackedBytes(value string) error {
|
|
||||||
if err := w.writePackedBytesImpl(value, token.NIBBLE_8); err != nil {
|
|
||||||
if err := w.writePackedBytesImpl(value, token.HEX_8); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) writePackedBytesImpl(value string, dataType int) error {
|
|
||||||
numBytes := len(value)
|
|
||||||
if numBytes > token.PACKED_MAX {
|
|
||||||
return fmt.Errorf("too many bytes to pack: %d", numBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.pushByte(byte(dataType))
|
|
||||||
|
|
||||||
x := 0
|
|
||||||
if numBytes%2 != 0 {
|
|
||||||
x = 128
|
|
||||||
}
|
|
||||||
w.pushByte(byte(x | int(math.Ceil(float64(numBytes)/2.0))))
|
|
||||||
for i, l := 0, numBytes/2; i < l; i++ {
|
|
||||||
b, err := w.packBytePair(dataType, value[2*i:2*i+1], value[2*i+1:2*i+2])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.pushByte(byte(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numBytes % 2) != 0 {
|
|
||||||
b, err := w.packBytePair(dataType, value[numBytes-1:], "\x00")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w.pushByte(byte(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *binaryEncoder) packBytePair(packType int, part1, part2 string) (int, error) {
|
|
||||||
if packType == token.NIBBLE_8 {
|
|
||||||
n1, err := packNibble(part1)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n2, err := packNibble(part2)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return (n1 << 4) | n2, nil
|
|
||||||
} else if packType == token.HEX_8 {
|
|
||||||
n1, err := packHex(part1)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n2, err := packHex(part2)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return (n1 << 4) | n2, nil
|
|
||||||
} else {
|
|
||||||
return 0, fmt.Errorf("invalid pack type (%d) for byte pair: %s / %s", packType, part1, part2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func packNibble(value string) (int, error) {
|
|
||||||
if value >= "0" && value <= "9" {
|
|
||||||
return strconv.Atoi(value)
|
|
||||||
} else if value == "-" {
|
|
||||||
return 10, nil
|
|
||||||
} else if value == "." {
|
|
||||||
return 11, nil
|
|
||||||
} else if value == "\x00" {
|
|
||||||
return 15, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, fmt.Errorf("invalid string to pack as nibble: %v", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func packHex(value string) (int, error) {
|
|
||||||
if (value >= "0" && value <= "9") || (value >= "A" && value <= "F") || (value >= "a" && value <= "f") {
|
|
||||||
d, err := strconv.ParseInt(value, 16, 0)
|
|
||||||
return int(d), err
|
|
||||||
} else if value == "\x00" {
|
|
||||||
return 15, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, fmt.Errorf("invalid string to pack as hex: %v", value)
|
|
||||||
}
|
|
106
vendor/github.com/Rhymen/go-whatsapp/binary/node.go
generated
vendored
106
vendor/github.com/Rhymen/go-whatsapp/binary/node.go
generated
vendored
@ -1,106 +0,0 @@
|
|||||||
package binary
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
pb "github.com/Rhymen/go-whatsapp/binary/proto"
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Node struct {
|
|
||||||
Description string
|
|
||||||
Attributes map[string]string
|
|
||||||
Content interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Marshal(n Node) ([]byte, error) {
|
|
||||||
if n.Attributes != nil && n.Content != nil {
|
|
||||||
a, err := marshalMessageArray(n.Content.([]interface{}))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
n.Content = a
|
|
||||||
}
|
|
||||||
|
|
||||||
w := NewEncoder()
|
|
||||||
if err := w.WriteNode(n); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.GetData(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalMessageArray(messages []interface{}) ([]Node, error) {
|
|
||||||
ret := make([]Node, len(messages))
|
|
||||||
|
|
||||||
for i, m := range messages {
|
|
||||||
if wmi, ok := m.(*pb.WebMessageInfo); ok {
|
|
||||||
b, err := marshalWebMessageInfo(wmi)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
ret[i] = Node{"message", nil, b}
|
|
||||||
} else {
|
|
||||||
ret[i], ok = m.(Node)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("invalid Node")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshalWebMessageInfo(p *pb.WebMessageInfo) ([]byte, error) {
|
|
||||||
b, err := proto.Marshal(p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Unmarshal(data []byte) (*Node, error) {
|
|
||||||
r := NewDecoder(data)
|
|
||||||
n, err := r.ReadNode()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if n != nil && n.Attributes != nil && n.Content != nil {
|
|
||||||
nContent, ok := n.Content.([]Node)
|
|
||||||
if ok {
|
|
||||||
n.Content, err = unmarshalMessageArray(nContent)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalMessageArray(messages []Node) ([]interface{}, error) {
|
|
||||||
ret := make([]interface{}, len(messages))
|
|
||||||
|
|
||||||
for i, msg := range messages {
|
|
||||||
if msg.Description == "message" {
|
|
||||||
info, err := unmarshalWebMessageInfo(msg.Content.([]byte))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ret[i] = info
|
|
||||||
} else {
|
|
||||||
ret[i] = msg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func unmarshalWebMessageInfo(msg []byte) (*pb.WebMessageInfo, error) {
|
|
||||||
message := &pb.WebMessageInfo{}
|
|
||||||
err := proto.Unmarshal(msg, message)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return message, nil
|
|
||||||
}
|
|
12807
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
generated
vendored
12807
vendor/github.com/Rhymen/go-whatsapp/binary/proto/def.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
81
vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
generated
vendored
81
vendor/github.com/Rhymen/go-whatsapp/binary/token/token.go
generated
vendored
@ -1,81 +0,0 @@
|
|||||||
package token
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
var SingleByteTokens = [...]string{"", "", "", "200", "400", "404", "500", "501", "502", "action", "add",
|
|
||||||
"after", "archive", "author", "available", "battery", "before", "body",
|
|
||||||
"broadcast", "chat", "clear", "code", "composing", "contacts", "count",
|
|
||||||
"create", "debug", "delete", "demote", "duplicate", "encoding", "error",
|
|
||||||
"false", "filehash", "from", "g.us", "group", "groups_v2", "height", "id",
|
|
||||||
"image", "in", "index", "invis", "item", "jid", "kind", "last", "leave",
|
|
||||||
"live", "log", "media", "message", "mimetype", "missing", "modify", "name",
|
|
||||||
"notification", "notify", "out", "owner", "participant", "paused",
|
|
||||||
"picture", "played", "presence", "preview", "promote", "query", "raw",
|
|
||||||
"read", "receipt", "received", "recipient", "recording", "relay",
|
|
||||||
"remove", "response", "resume", "retry", "s.whatsapp.net", "seconds",
|
|
||||||
"set", "size", "status", "subject", "subscribe", "t", "text", "to", "true",
|
|
||||||
"type", "unarchive", "unavailable", "url", "user", "value", "web", "width",
|
|
||||||
"mute", "read_only", "admin", "creator", "short", "update", "powersave",
|
|
||||||
"checksum", "epoch", "block", "previous", "409", "replaced", "reason",
|
|
||||||
"spam", "modify_tag", "message_info", "delivery", "emoji", "title",
|
|
||||||
"description", "canonical-url", "matched-text", "star", "unstar",
|
|
||||||
"media_key", "filename", "identity", "unread", "page", "page_count",
|
|
||||||
"search", "media_message", "security", "call_log", "profile", "ciphertext",
|
|
||||||
"invite", "gif", "vcard", "frequent", "privacy", "blacklist", "whitelist",
|
|
||||||
"verify", "location", "document", "elapsed", "revoke_invite", "expiration",
|
|
||||||
"unsubscribe", "disable", "vname", "old_jid", "new_jid", "announcement",
|
|
||||||
"locked", "prop", "label", "color", "call", "offer", "call-id",
|
|
||||||
"quick_reply", "sticker", "pay_t", "accept", "reject", "sticker_pack",
|
|
||||||
"invalid", "canceled", "missed", "connected", "result", "audio",
|
|
||||||
"video", "recent"}
|
|
||||||
|
|
||||||
var doubleByteTokens = [...]string{}
|
|
||||||
|
|
||||||
func GetSingleToken(i int) (string, error) {
|
|
||||||
if i < 3 || i >= len(SingleByteTokens) {
|
|
||||||
return "", fmt.Errorf("index out of single byte token bounds %d", i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return SingleByteTokens[i], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDoubleToken(index1 int, index2 int) (string, error) {
|
|
||||||
n := 256*index1 + index2
|
|
||||||
if n < 0 || n >= len(doubleByteTokens) {
|
|
||||||
return "", fmt.Errorf("index out of double byte token bounds %d", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
return doubleByteTokens[n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func IndexOfSingleToken(token string) int {
|
|
||||||
for i, t := range SingleByteTokens {
|
|
||||||
if t == token {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
LIST_EMPTY = 0
|
|
||||||
STREAM_END = 2
|
|
||||||
DICTIONARY_0 = 236
|
|
||||||
DICTIONARY_1 = 237
|
|
||||||
DICTIONARY_2 = 238
|
|
||||||
DICTIONARY_3 = 239
|
|
||||||
LIST_8 = 248
|
|
||||||
LIST_16 = 249
|
|
||||||
JID_PAIR = 250
|
|
||||||
HEX_8 = 251
|
|
||||||
BINARY_8 = 252
|
|
||||||
BINARY_20 = 253
|
|
||||||
BINARY_32 = 254
|
|
||||||
NIBBLE_8 = 255
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
PACKED_MAX = 254
|
|
||||||
SINGLE_BYTE_MAX = 256
|
|
||||||
)
|
|
183
vendor/github.com/Rhymen/go-whatsapp/chat_history.go
generated
vendored
183
vendor/github.com/Rhymen/go-whatsapp/chat_history.go
generated
vendored
@ -1,183 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary"
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary/proto"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MessageOffsetInfo struct {
|
|
||||||
FirstMessageId string
|
|
||||||
FirstMessageOwner bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeMessages(n *binary.Node) []*proto.WebMessageInfo {
|
|
||||||
|
|
||||||
var messages = make([]*proto.WebMessageInfo, 0)
|
|
||||||
|
|
||||||
if n == nil || n.Attributes == nil || n.Content == nil {
|
|
||||||
return messages
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, msg := range n.Content.([]interface{}) {
|
|
||||||
switch msg.(type) {
|
|
||||||
case *proto.WebMessageInfo:
|
|
||||||
messages = append(messages, msg.(*proto.WebMessageInfo))
|
|
||||||
default:
|
|
||||||
log.Println("decodeMessages: Non WebMessage encountered")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return messages
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadChatMessages is useful to "scroll" messages, loading by count at a time
|
|
||||||
// if handlers == nil the func will use default handlers
|
|
||||||
// if after == true LoadChatMessages will load messages after the specified messageId, otherwise it will return
|
|
||||||
// message before the messageId
|
|
||||||
func (wac *Conn) LoadChatMessages(jid string, count int, messageId string, owner bool, after bool, handlers ...Handler) error {
|
|
||||||
if count <= 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if handlers == nil {
|
|
||||||
handlers = wac.handler
|
|
||||||
}
|
|
||||||
|
|
||||||
kind := "before"
|
|
||||||
if after {
|
|
||||||
kind = "after"
|
|
||||||
}
|
|
||||||
|
|
||||||
node, err := wac.query("message", jid, messageId, kind,
|
|
||||||
strconv.FormatBool(owner), "", count, 0)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
wac.handleWithCustomHandlers(err, handlers)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, msg := range decodeMessages(node) {
|
|
||||||
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
|
|
||||||
wac.handleWithCustomHandlers(msg, handlers)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadFullChatHistory loads full chat history for the given jid
|
|
||||||
// chunkSize = how many messages to load with one query; if handlers == nil the func will use default handlers;
|
|
||||||
// pauseBetweenQueries = how much time to sleep between queries
|
|
||||||
func (wac *Conn) LoadFullChatHistory(jid string, chunkSize int,
|
|
||||||
pauseBetweenQueries time.Duration, handlers ...Handler) {
|
|
||||||
if chunkSize <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if handlers == nil {
|
|
||||||
handlers = wac.handler
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeMsg := ""
|
|
||||||
beforeMsgIsOwner := true
|
|
||||||
|
|
||||||
for {
|
|
||||||
node, err := wac.query("message", jid, beforeMsg, "before",
|
|
||||||
strconv.FormatBool(beforeMsgIsOwner), "", chunkSize, 0)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
wac.handleWithCustomHandlers(err, handlers)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
msgs := decodeMessages(node)
|
|
||||||
for _, msg := range msgs {
|
|
||||||
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
|
|
||||||
wac.handleWithCustomHandlers(msg, handlers)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(msgs) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeMsg = *msgs[0].Key.Id
|
|
||||||
beforeMsgIsOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe
|
|
||||||
}
|
|
||||||
|
|
||||||
<-time.After(pauseBetweenQueries)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadFullChatHistoryAfter loads all messages after the specified messageId
|
|
||||||
// useful to "catch up" with the message history after some specified message
|
|
||||||
func (wac *Conn) LoadFullChatHistoryAfter(jid string, messageId string, chunkSize int,
|
|
||||||
pauseBetweenQueries time.Duration, handlers ...Handler) {
|
|
||||||
|
|
||||||
if chunkSize <= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if handlers == nil {
|
|
||||||
handlers = wac.handler
|
|
||||||
}
|
|
||||||
|
|
||||||
msgOwner := true
|
|
||||||
prevNotFound := false
|
|
||||||
|
|
||||||
for {
|
|
||||||
node, err := wac.query("message", jid, messageId, "after",
|
|
||||||
strconv.FormatBool(msgOwner), "", chunkSize, 0)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
// Whatsapp will return 404 status when there is wrong owner flag on the requested message id
|
|
||||||
if err == ErrServerRespondedWith404 {
|
|
||||||
|
|
||||||
// this will detect two consecutive "not found" errors.
|
|
||||||
// this is done to prevent infinite loop when wrong message id supplied
|
|
||||||
if prevNotFound {
|
|
||||||
log.Println("LoadFullChatHistoryAfter: could not retrieve any messages, wrong message id?")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
prevNotFound = true
|
|
||||||
|
|
||||||
// try to reverse the owner flag and retry
|
|
||||||
if msgOwner {
|
|
||||||
// reverse initial msgOwner value and retry
|
|
||||||
msgOwner = false
|
|
||||||
|
|
||||||
<-time.After(time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the error isn't a 404 error, pass it to the error handler
|
|
||||||
wac.handleWithCustomHandlers(err, handlers)
|
|
||||||
} else {
|
|
||||||
|
|
||||||
msgs := decodeMessages(node)
|
|
||||||
for _, msg := range msgs {
|
|
||||||
wac.handleWithCustomHandlers(ParseProtoMessage(msg), handlers)
|
|
||||||
wac.handleWithCustomHandlers(msg, handlers)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(msgs) != chunkSize {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
messageId = *msgs[0].Key.Id
|
|
||||||
msgOwner = msgs[0].Key.FromMe != nil && *msgs[0].Key.FromMe
|
|
||||||
}
|
|
||||||
|
|
||||||
// message was found
|
|
||||||
prevNotFound = false
|
|
||||||
|
|
||||||
<-time.After(pauseBetweenQueries)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
302
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
302
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
@ -1,302 +0,0 @@
|
|||||||
//Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
|
|
||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type metric byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
debugLog metric = iota + 1
|
|
||||||
queryResume
|
|
||||||
queryReceipt
|
|
||||||
queryMedia
|
|
||||||
queryChat
|
|
||||||
queryContacts
|
|
||||||
queryMessages
|
|
||||||
presence
|
|
||||||
presenceSubscribe
|
|
||||||
group
|
|
||||||
read
|
|
||||||
chat
|
|
||||||
received
|
|
||||||
pic
|
|
||||||
status
|
|
||||||
message
|
|
||||||
queryActions
|
|
||||||
block
|
|
||||||
queryGroup
|
|
||||||
queryPreview
|
|
||||||
queryEmoji
|
|
||||||
queryMessageInfo
|
|
||||||
spam
|
|
||||||
querySearch
|
|
||||||
queryIdentity
|
|
||||||
queryUrl
|
|
||||||
profile
|
|
||||||
contact
|
|
||||||
queryVcard
|
|
||||||
queryStatus
|
|
||||||
queryStatusUpdate
|
|
||||||
privacyStatus
|
|
||||||
queryLiveLocations
|
|
||||||
liveLocation
|
|
||||||
queryVname
|
|
||||||
queryLabels
|
|
||||||
call
|
|
||||||
queryCall
|
|
||||||
queryQuickReplies
|
|
||||||
)
|
|
||||||
|
|
||||||
type flag byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
ignore flag = 1 << (7 - iota)
|
|
||||||
ackRequest
|
|
||||||
available
|
|
||||||
notAvailable
|
|
||||||
expires
|
|
||||||
skipOffline
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package.
|
|
||||||
It holds all necessary information to make the package work internally.
|
|
||||||
*/
|
|
||||||
type Conn struct {
|
|
||||||
ws *websocketWrapper
|
|
||||||
listener *listenerWrapper
|
|
||||||
|
|
||||||
connected bool
|
|
||||||
loggedIn bool
|
|
||||||
wg *sync.WaitGroup
|
|
||||||
|
|
||||||
session *Session
|
|
||||||
sessionLock uint32
|
|
||||||
handler []Handler
|
|
||||||
msgCount int
|
|
||||||
msgTimeout time.Duration
|
|
||||||
Info *Info
|
|
||||||
Store *Store
|
|
||||||
ServerLastSeen time.Time
|
|
||||||
|
|
||||||
timeTag string // last 3 digits obtained after a successful login takeover
|
|
||||||
|
|
||||||
longClientName string
|
|
||||||
shortClientName string
|
|
||||||
clientVersion string
|
|
||||||
|
|
||||||
loginSessionLock sync.RWMutex
|
|
||||||
Proxy func(*http.Request) (*url.URL, error)
|
|
||||||
|
|
||||||
writerLock sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
type websocketWrapper struct {
|
|
||||||
sync.Mutex
|
|
||||||
conn *websocket.Conn
|
|
||||||
close chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type listenerWrapper struct {
|
|
||||||
sync.RWMutex
|
|
||||||
m map[string]chan string
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established.
|
|
||||||
The goroutine for handling incoming messages is started
|
|
||||||
*/
|
|
||||||
func NewConn(timeout time.Duration) (*Conn, error) {
|
|
||||||
return NewConnWithOptions(&Options{
|
|
||||||
Timeout: timeout,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConnWithProxy Create a new connect with a given timeout and a http proxy.
|
|
||||||
func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL, error)) (*Conn, error) {
|
|
||||||
return NewConnWithOptions(&Options{
|
|
||||||
Timeout: timeout,
|
|
||||||
Proxy: proxy,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConnWithOptions Create a new connect with a given options.
|
|
||||||
type Options struct {
|
|
||||||
Proxy func(*http.Request) (*url.URL, error)
|
|
||||||
Timeout time.Duration
|
|
||||||
Handler []Handler
|
|
||||||
ShortClientName string
|
|
||||||
LongClientName string
|
|
||||||
ClientVersion string
|
|
||||||
Store *Store
|
|
||||||
}
|
|
||||||
func NewConnWithOptions(opt *Options) (*Conn, error) {
|
|
||||||
if opt == nil {
|
|
||||||
return nil, ErrOptionsNotProvided
|
|
||||||
}
|
|
||||||
wac := &Conn{
|
|
||||||
handler: make([]Handler, 0),
|
|
||||||
msgCount: 0,
|
|
||||||
msgTimeout: opt.Timeout,
|
|
||||||
Store: newStore(),
|
|
||||||
longClientName: "github.com/Rhymen/go-whatsapp",
|
|
||||||
shortClientName: "go-whatsapp",
|
|
||||||
clientVersion: "0.1.0",
|
|
||||||
}
|
|
||||||
if opt.Handler != nil {
|
|
||||||
wac.handler = opt.Handler
|
|
||||||
}
|
|
||||||
if opt.Store != nil {
|
|
||||||
wac.Store = opt.Store
|
|
||||||
}
|
|
||||||
if opt.Proxy != nil {
|
|
||||||
wac.Proxy = opt.Proxy
|
|
||||||
}
|
|
||||||
if len(opt.ShortClientName) != 0 {
|
|
||||||
wac.shortClientName = opt.ShortClientName
|
|
||||||
}
|
|
||||||
if len(opt.LongClientName) != 0 {
|
|
||||||
wac.longClientName = opt.LongClientName
|
|
||||||
}
|
|
||||||
if len(opt.ClientVersion) != 0 {
|
|
||||||
wac.clientVersion = opt.ClientVersion
|
|
||||||
}
|
|
||||||
return wac, wac.connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect should be guarded with wsWriteMutex
|
|
||||||
func (wac *Conn) connect() (err error) {
|
|
||||||
if wac.connected {
|
|
||||||
return ErrAlreadyConnected
|
|
||||||
}
|
|
||||||
wac.connected = true
|
|
||||||
defer func() { // set connected to false on error
|
|
||||||
if err != nil {
|
|
||||||
wac.connected = false
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
dialer := &websocket.Dialer{
|
|
||||||
ReadBufferSize: 0,
|
|
||||||
WriteBufferSize: 0,
|
|
||||||
HandshakeTimeout: wac.msgTimeout,
|
|
||||||
Proxy: wac.Proxy,
|
|
||||||
}
|
|
||||||
|
|
||||||
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
|
|
||||||
wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "couldn't dial whatsapp web websocket")
|
|
||||||
}
|
|
||||||
|
|
||||||
wsConn.SetCloseHandler(func(code int, text string) error {
|
|
||||||
// from default CloseHandler
|
|
||||||
message := websocket.FormatCloseMessage(code, "")
|
|
||||||
err := wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
|
|
||||||
|
|
||||||
// our close handling
|
|
||||||
_, _ = wac.Disconnect()
|
|
||||||
wac.handle(&ErrConnectionClosed{Code: code, Text: text})
|
|
||||||
return err
|
|
||||||
})
|
|
||||||
|
|
||||||
wac.ws = &websocketWrapper{
|
|
||||||
conn: wsConn,
|
|
||||||
close: make(chan struct{}),
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.listener = &listenerWrapper{
|
|
||||||
m: make(map[string]chan string),
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.wg = &sync.WaitGroup{}
|
|
||||||
wac.wg.Add(2)
|
|
||||||
go wac.readPump()
|
|
||||||
go wac.keepAlive(20000, 60000)
|
|
||||||
|
|
||||||
wac.loggedIn = false
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Disconnect() (Session, error) {
|
|
||||||
if !wac.connected {
|
|
||||||
return Session{}, ErrNotConnected
|
|
||||||
}
|
|
||||||
wac.connected = false
|
|
||||||
wac.loggedIn = false
|
|
||||||
|
|
||||||
close(wac.ws.close) //signal close
|
|
||||||
wac.wg.Wait() //wait for close
|
|
||||||
|
|
||||||
err := wac.ws.conn.Close()
|
|
||||||
wac.ws = nil
|
|
||||||
|
|
||||||
if wac.session == nil {
|
|
||||||
return Session{}, err
|
|
||||||
}
|
|
||||||
return *wac.session, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) AdminTest() (bool, error) {
|
|
||||||
if !wac.connected {
|
|
||||||
return false, ErrNotConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
if !wac.loggedIn {
|
|
||||||
return false, ErrInvalidSession
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := wac.sendAdminTest()
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
|
|
||||||
defer wac.wg.Done()
|
|
||||||
|
|
||||||
for {
|
|
||||||
err := wac.sendKeepAlive()
|
|
||||||
if err != nil {
|
|
||||||
wac.handle(errors.Wrap(err, "keepAlive failed"))
|
|
||||||
//TODO: Consequences?
|
|
||||||
}
|
|
||||||
interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
|
|
||||||
select {
|
|
||||||
case <-time.After(time.Duration(interval) * time.Millisecond):
|
|
||||||
case <-wac.ws.close:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsConnected returns whether the server connection is established or not
|
|
||||||
func (wac *Conn) IsConnected() bool {
|
|
||||||
return wac.connected
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetConnected returns whether the server connection is established or not
|
|
||||||
//
|
|
||||||
// Deprecated: function name is not go idiomatic, use IsConnected instead
|
|
||||||
func (wac *Conn) GetConnected() bool {
|
|
||||||
return wac.connected
|
|
||||||
}
|
|
||||||
|
|
||||||
//IsLoggedIn returns whether the you are logged in or not
|
|
||||||
func (wac *Conn) IsLoggedIn() bool {
|
|
||||||
return wac.loggedIn
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLoggedIn returns whether the you are logged in or not
|
|
||||||
//
|
|
||||||
// Deprecated: function name is not go idiomatic, use IsLoggedIn instead.
|
|
||||||
func (wac *Conn) GetLoggedIn() bool {
|
|
||||||
return wac.loggedIn
|
|
||||||
}
|
|
322
vendor/github.com/Rhymen/go-whatsapp/contact.go
generated
vendored
322
vendor/github.com/Rhymen/go-whatsapp/contact.go
generated
vendored
@ -1,322 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Presence string
|
|
||||||
|
|
||||||
const (
|
|
||||||
PresenceAvailable Presence = "available"
|
|
||||||
PresenceUnavailable Presence = "unavailable"
|
|
||||||
PresenceComposing Presence = "composing"
|
|
||||||
PresenceRecording Presence = "recording"
|
|
||||||
PresencePaused Presence = "paused"
|
|
||||||
)
|
|
||||||
|
|
||||||
//TODO: filename? WhatsApp uses Store.Contacts for these functions
|
|
||||||
// functions probably shouldn't return a string, maybe build a struct / return json
|
|
||||||
// check for further queries
|
|
||||||
func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) {
|
|
||||||
data := []interface{}{"query", "ProfilePicThumb", jid}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) GetStatus(jid string) (<-chan string, error) {
|
|
||||||
data := []interface{}{"query", "Status", jid}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) {
|
|
||||||
data := []interface{}{"action", "presence", "subscribe", jid}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) {
|
|
||||||
return wac.query("search", "", "", "", "", search, count, page)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) LoadMessages(jid, messageId string, count int) (*binary.Node, error) {
|
|
||||||
return wac.query("message", jid, "", "before", "true", "", count, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) LoadMessagesBefore(jid, messageId string, count int) (*binary.Node, error) {
|
|
||||||
return wac.query("message", jid, messageId, "before", "true", "", count, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) LoadMessagesAfter(jid, messageId string, count int) (*binary.Node, error) {
|
|
||||||
return wac.query("message", jid, messageId, "after", "true", "", count, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) LoadMediaInfo(jid, messageId, owner string) (*binary.Node, error) {
|
|
||||||
return wac.query("media", jid, messageId, "", owner, "", 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error) {
|
|
||||||
ts := time.Now().Unix()
|
|
||||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
|
||||||
|
|
||||||
content := binary.Node{
|
|
||||||
Description: "presence",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": string(presence),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
switch presence {
|
|
||||||
case PresenceComposing:
|
|
||||||
fallthrough
|
|
||||||
case PresenceRecording:
|
|
||||||
fallthrough
|
|
||||||
case PresencePaused:
|
|
||||||
content.Attributes["to"] = jid
|
|
||||||
}
|
|
||||||
|
|
||||||
n := binary.Node{
|
|
||||||
Description: "action",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": "set",
|
|
||||||
"epoch": strconv.Itoa(wac.msgCount),
|
|
||||||
},
|
|
||||||
Content: []interface{}{content},
|
|
||||||
}
|
|
||||||
|
|
||||||
return wac.writeBinary(n, group, ignore, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Exist(jid string) (<-chan string, error) {
|
|
||||||
data := []interface{}{"query", "exist", jid}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Emoji() (*binary.Node, error) {
|
|
||||||
return wac.query("emoji", "", "", "", "", "", 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Contacts() (*binary.Node, error) {
|
|
||||||
return wac.query("contacts", "", "", "", "", "", 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Chats() (*binary.Node, error) {
|
|
||||||
return wac.query("chat", "", "", "", "", "", 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Read(jid, id string) (<-chan string, error) {
|
|
||||||
ts := time.Now().Unix()
|
|
||||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
|
||||||
|
|
||||||
n := binary.Node{
|
|
||||||
Description: "action",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": "set",
|
|
||||||
"epoch": strconv.Itoa(wac.msgCount),
|
|
||||||
},
|
|
||||||
Content: []interface{}{binary.Node{
|
|
||||||
Description: "read",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"count": "1",
|
|
||||||
"index": id,
|
|
||||||
"jid": jid,
|
|
||||||
"owner": "false",
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
return wac.writeBinary(n, group, ignore, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) query(t, jid, messageId, kind, owner, search string, count, page int) (*binary.Node, error) {
|
|
||||||
ts := time.Now().Unix()
|
|
||||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
|
||||||
|
|
||||||
n := binary.Node{
|
|
||||||
Description: "query",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": t,
|
|
||||||
"epoch": strconv.Itoa(wac.msgCount),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if jid != "" {
|
|
||||||
n.Attributes["jid"] = jid
|
|
||||||
}
|
|
||||||
|
|
||||||
if messageId != "" {
|
|
||||||
n.Attributes["index"] = messageId
|
|
||||||
}
|
|
||||||
|
|
||||||
if kind != "" {
|
|
||||||
n.Attributes["kind"] = kind
|
|
||||||
}
|
|
||||||
|
|
||||||
if owner != "" {
|
|
||||||
n.Attributes["owner"] = owner
|
|
||||||
}
|
|
||||||
|
|
||||||
if search != "" {
|
|
||||||
n.Attributes["search"] = search
|
|
||||||
}
|
|
||||||
|
|
||||||
if count != 0 {
|
|
||||||
n.Attributes["count"] = strconv.Itoa(count)
|
|
||||||
}
|
|
||||||
|
|
||||||
if page != 0 {
|
|
||||||
n.Attributes["page"] = strconv.Itoa(page)
|
|
||||||
}
|
|
||||||
|
|
||||||
metric := group
|
|
||||||
if t == "media" {
|
|
||||||
metric = queryMedia
|
|
||||||
}
|
|
||||||
|
|
||||||
ch, err := wac.writeBinary(n, metric, ignore, tag)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := wac.decryptBinaryMessage([]byte(<-ch))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: use parseProtoMessage
|
|
||||||
return msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) setGroup(t, jid, subject string, participants []string) (<-chan string, error) {
|
|
||||||
ts := time.Now().Unix()
|
|
||||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
|
||||||
|
|
||||||
//TODO: get proto or improve encoder to handle []interface{}
|
|
||||||
|
|
||||||
p := buildParticipantNodes(participants)
|
|
||||||
|
|
||||||
g := binary.Node{
|
|
||||||
Description: "group",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"author": wac.session.Wid,
|
|
||||||
"id": tag,
|
|
||||||
"type": t,
|
|
||||||
},
|
|
||||||
Content: p,
|
|
||||||
}
|
|
||||||
|
|
||||||
if jid != "" {
|
|
||||||
g.Attributes["jid"] = jid
|
|
||||||
}
|
|
||||||
|
|
||||||
if subject != "" {
|
|
||||||
g.Attributes["subject"] = subject
|
|
||||||
}
|
|
||||||
|
|
||||||
n := binary.Node{
|
|
||||||
Description: "action",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": "set",
|
|
||||||
"epoch": strconv.Itoa(wac.msgCount),
|
|
||||||
},
|
|
||||||
Content: []interface{}{g},
|
|
||||||
}
|
|
||||||
|
|
||||||
return wac.writeBinary(n, group, ignore, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildParticipantNodes(participants []string) []binary.Node {
|
|
||||||
l := len(participants)
|
|
||||||
if participants == nil || l == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
p := make([]binary.Node, len(participants))
|
|
||||||
for i, participant := range participants {
|
|
||||||
p[i] = binary.Node{
|
|
||||||
Description: "participant",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"jid": participant,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) BlockContact(jid string) (<-chan string, error) {
|
|
||||||
return wac.handleBlockContact("add", jid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) UnblockContact(jid string) (<-chan string, error) {
|
|
||||||
return wac.handleBlockContact("remove", jid)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) handleBlockContact(action, jid string) (<-chan string, error) {
|
|
||||||
ts := time.Now().Unix()
|
|
||||||
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
|
||||||
|
|
||||||
netsplit := strings.Split(jid, "@")
|
|
||||||
cusjid := netsplit[0] + "@c.us"
|
|
||||||
|
|
||||||
n := binary.Node{
|
|
||||||
Description: "action",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": "set",
|
|
||||||
"epoch": strconv.Itoa(wac.msgCount),
|
|
||||||
},
|
|
||||||
Content: []interface{}{
|
|
||||||
binary.Node{
|
|
||||||
Description: "block",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": action,
|
|
||||||
},
|
|
||||||
Content: []binary.Node{
|
|
||||||
{
|
|
||||||
Description: "user",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"jid": cusjid,
|
|
||||||
},
|
|
||||||
Content: nil,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return wac.writeBinary(n, contact, ignore, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search product details on order
|
|
||||||
func (wac *Conn) SearchProductDetails(id, orderId, token string) (<-chan string, error) {
|
|
||||||
data := []interface{}{"query", "order", map[string]string{
|
|
||||||
"id": id,
|
|
||||||
"orderId": orderId,
|
|
||||||
"imageHeight": strconv.Itoa(80),
|
|
||||||
"imageWidth": strconv.Itoa(80),
|
|
||||||
"token": token,
|
|
||||||
}}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Order search and get product catalog reh
|
|
||||||
func (wac *Conn) SearchOrder(catalogWid, stanzaId string) (<-chan string, error) {
|
|
||||||
data := []interface{}{"query", "bizCatalog", map[string]string{
|
|
||||||
"catalogWid": catalogWid,
|
|
||||||
"limit": strconv.Itoa(10),
|
|
||||||
"height": strconv.Itoa(100),
|
|
||||||
"width": strconv.Itoa(100),
|
|
||||||
"stanza_id": stanzaId,
|
|
||||||
"type": "get_product_catalog_reh",
|
|
||||||
}}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Company details for Whatsapp Business
|
|
||||||
func (wac *Conn) BusinessProfile(wid string) (<-chan string, error) {
|
|
||||||
query := map[string]string{
|
|
||||||
"wid": wid,
|
|
||||||
}
|
|
||||||
data := []interface{}{"query", "businessProfile", []map[string]string{query}}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
44
vendor/github.com/Rhymen/go-whatsapp/crypto/curve25519/curve.go
generated
vendored
44
vendor/github.com/Rhymen/go-whatsapp/crypto/curve25519/curve.go
generated
vendored
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
In cryptography, Curve25519 is an elliptic curve offering 128 bits of security and designed for use with the elliptic
|
|
||||||
curve Diffie–Hellman (ECDH) key agreement scheme. It is one of the fastest ECC curves and is not covered by any known
|
|
||||||
patents. The reference implementation is public domain software. The original Curve25519 paper defined it
|
|
||||||
as a Diffie–Hellman (DH) function.
|
|
||||||
*/
|
|
||||||
package curve25519
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"golang.org/x/crypto/curve25519"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
GenerateKey generates a public private key pair using Curve25519.
|
|
||||||
*/
|
|
||||||
func GenerateKey() (privateKey *[32]byte, publicKey *[32]byte, err error) {
|
|
||||||
var pub, priv [32]byte
|
|
||||||
|
|
||||||
_, err = io.ReadFull(rand.Reader, priv[:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
priv[0] &= 248
|
|
||||||
priv[31] &= 127
|
|
||||||
priv[31] |= 64
|
|
||||||
|
|
||||||
curve25519.ScalarBaseMult(&pub, &priv)
|
|
||||||
|
|
||||||
return &priv, &pub, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
GenerateSharedSecret generates the shared secret with a given public private key pair.
|
|
||||||
*/
|
|
||||||
func GenerateSharedSecret(priv, pub [32]byte) []byte {
|
|
||||||
var secret [32]byte
|
|
||||||
|
|
||||||
curve25519.ScalarMult(&secret, &priv, &pub)
|
|
||||||
|
|
||||||
return secret[:]
|
|
||||||
}
|
|
47
vendor/github.com/Rhymen/go-whatsapp/crypto/hkdf/hkdf.go
generated
vendored
47
vendor/github.com/Rhymen/go-whatsapp/crypto/hkdf/hkdf.go
generated
vendored
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
HKDF is a simple key derivation function (KDF) based on
|
|
||||||
a hash-based message authentication code (HMAC). It was initially proposed by its authors as a building block in
|
|
||||||
various protocols and applications, as well as to discourage the proliferation of multiple KDF mechanisms.
|
|
||||||
The main approach HKDF follows is the "extract-then-expand" paradigm, where the KDF logically consists of two modules:
|
|
||||||
the first stage takes the input keying material and "extracts" from it a fixed-length pseudorandom key, and then the
|
|
||||||
second stage "expands" this key into several additional pseudorandom keys (the output of the KDF).
|
|
||||||
*/
|
|
||||||
package hkdf
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
|
||||||
"golang.org/x/crypto/hkdf"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
Expand expands a given key with the HKDF algorithm.
|
|
||||||
*/
|
|
||||||
func Expand(key []byte, length int, info string) ([]byte, error) {
|
|
||||||
var h io.Reader
|
|
||||||
if info == "" {
|
|
||||||
/*
|
|
||||||
Only used during initial login
|
|
||||||
Pseudorandom Key is provided by server and has not to be created
|
|
||||||
*/
|
|
||||||
h = hkdf.Expand(sha256.New, key, []byte(info))
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
Used every other time
|
|
||||||
Pseudorandom Key is created during kdf.New
|
|
||||||
This is the normal that crypto/hkdf is used
|
|
||||||
*/
|
|
||||||
h = hkdf.New(sha256.New, key, nil, []byte(info))
|
|
||||||
}
|
|
||||||
out := make([]byte, length)
|
|
||||||
n, err := io.ReadAtLeast(h, out, length)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if n != length {
|
|
||||||
return nil, fmt.Errorf("new key to short")
|
|
||||||
}
|
|
||||||
|
|
||||||
return out, nil
|
|
||||||
}
|
|
42
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
42
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
@ -1,42 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrAlreadyConnected = errors.New("already connected")
|
|
||||||
ErrAlreadyLoggedIn = errors.New("already logged in")
|
|
||||||
ErrInvalidSession = errors.New("invalid session")
|
|
||||||
ErrLoginInProgress = errors.New("login or restore already running")
|
|
||||||
ErrNotConnected = errors.New("not connected")
|
|
||||||
ErrInvalidWsData = errors.New("received invalid data")
|
|
||||||
ErrInvalidWsState = errors.New("can't handle binary data when not logged in")
|
|
||||||
ErrConnectionTimeout = errors.New("connection timed out")
|
|
||||||
ErrMissingMessageTag = errors.New("no messageTag specified or to short")
|
|
||||||
ErrInvalidHmac = errors.New("invalid hmac")
|
|
||||||
ErrInvalidServerResponse = errors.New("invalid response received from server")
|
|
||||||
ErrServerRespondedWith404 = errors.New("server responded with status 404")
|
|
||||||
ErrInvalidWebsocket = errors.New("invalid websocket")
|
|
||||||
ErrMessageTypeNotImplemented = errors.New("message type not implemented")
|
|
||||||
ErrOptionsNotProvided = errors.New("new conn options not provided")
|
|
||||||
)
|
|
||||||
|
|
||||||
type ErrConnectionFailed struct {
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ErrConnectionFailed) Error() string {
|
|
||||||
return fmt.Sprintf("connection to WhatsApp servers failed: %v", e.Err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ErrConnectionClosed struct {
|
|
||||||
Code int
|
|
||||||
Text string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ErrConnectionClosed) Error() string {
|
|
||||||
return fmt.Sprintf("server closed connection,code: %d,text: %s", e.Code, e.Text)
|
|
||||||
}
|
|
90
vendor/github.com/Rhymen/go-whatsapp/group.go
generated
vendored
90
vendor/github.com/Rhymen/go-whatsapp/group.go
generated
vendored
@ -1,90 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) {
|
|
||||||
data := []interface{}{"query", "GroupMetadata", jid}
|
|
||||||
return wac.writeJson(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) CreateGroup(subject string, participants []string) (<-chan string, error) {
|
|
||||||
return wac.setGroup("create", "", subject, participants)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) UpdateGroupSubject(subject string, jid string) (<-chan string, error) {
|
|
||||||
return wac.setGroup("subject", jid, subject, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) SetAdmin(jid string, participants []string) (<-chan string, error) {
|
|
||||||
return wac.setGroup("promote", jid, "", participants)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) RemoveAdmin(jid string, participants []string) (<-chan string, error) {
|
|
||||||
return wac.setGroup("demote", jid, "", participants)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) AddMember(jid string, participants []string) (<-chan string, error) {
|
|
||||||
return wac.setGroup("add", jid, "", participants)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) RemoveMember(jid string, participants []string) (<-chan string, error) {
|
|
||||||
return wac.setGroup("remove", jid, "", participants)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) LeaveGroup(jid string) (<-chan string, error) {
|
|
||||||
return wac.setGroup("leave", jid, "", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) GroupInviteLink(jid string) (string, error) {
|
|
||||||
request := []interface{}{"query", "inviteCode", jid}
|
|
||||||
ch, err := wac.writeJson(request)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var response map[string]interface{}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case r := <-ch:
|
|
||||||
if err := json.Unmarshal([]byte(r), &response); err != nil {
|
|
||||||
return "", fmt.Errorf("error decoding response message: %v\n", err)
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return "", fmt.Errorf("request timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
if int(response["status"].(float64)) != 200 {
|
|
||||||
return "", fmt.Errorf("request responded with %d", response["status"])
|
|
||||||
}
|
|
||||||
|
|
||||||
return response["code"].(string), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) GroupAcceptInviteCode(code string) (jid string, err error) {
|
|
||||||
request := []interface{}{"action", "invite", code}
|
|
||||||
ch, err := wac.writeJson(request)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var response map[string]interface{}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case r := <-ch:
|
|
||||||
if err := json.Unmarshal([]byte(r), &response); err != nil {
|
|
||||||
return "", fmt.Errorf("error decoding response message: %v\n", err)
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return "", fmt.Errorf("request timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
if int(response["status"].(float64)) != 200 {
|
|
||||||
return "", fmt.Errorf("request responded with %d", response["status"])
|
|
||||||
}
|
|
||||||
|
|
||||||
return response["gid"].(string), nil
|
|
||||||
}
|
|
481
vendor/github.com/Rhymen/go-whatsapp/handler.go
generated
vendored
481
vendor/github.com/Rhymen/go-whatsapp/handler.go
generated
vendored
@ -1,481 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary"
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
The Handler interface is the minimal interface that needs to be implemented
|
|
||||||
to be accepted as a valid handler for our dispatching system.
|
|
||||||
The minimal handler is used to dispatch error messages. These errors occur on unexpected behavior by the websocket
|
|
||||||
connection or if we are unable to handle or interpret an incoming message. Error produced by user actions are not
|
|
||||||
dispatched through this handler. They are returned as an error on the specific function call.
|
|
||||||
*/
|
|
||||||
type Handler interface {
|
|
||||||
HandleError(err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type SyncHandler interface {
|
|
||||||
Handler
|
|
||||||
ShouldCallSynchronously() bool
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The TextMessageHandler interface needs to be implemented to receive text messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type TextMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleTextMessage(message TextMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The ImageMessageHandler interface needs to be implemented to receive image messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type ImageMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleImageMessage(message ImageMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The VideoMessageHandler interface needs to be implemented to receive video messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type VideoMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleVideoMessage(message VideoMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The AudioMessageHandler interface needs to be implemented to receive audio messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type AudioMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleAudioMessage(message AudioMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The DocumentMessageHandler interface needs to be implemented to receive document messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type DocumentMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleDocumentMessage(message DocumentMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The LiveLocationMessageHandler interface needs to be implemented to receive live location messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type LiveLocationMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleLiveLocationMessage(message LiveLocationMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The LocationMessageHandler interface needs to be implemented to receive location messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type LocationMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleLocationMessage(message LocationMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The StickerMessageHandler interface needs to be implemented to receive sticker messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type StickerMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleStickerMessage(message StickerMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The ContactMessageHandler interface needs to be implemented to receive contact messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type ContactMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleContactMessage(message ContactMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The ProductMessageHandler interface needs to be implemented to receive product messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type ProductMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleProductMessage(message ProductMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The OrderMessageHandler interface needs to be implemented to receive order messages dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type OrderMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleOrderMessage(message OrderMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The JsonMessageHandler interface needs to be implemented to receive json messages dispatched by the dispatcher.
|
|
||||||
These json messages contain status updates of every kind sent by WhatsAppWeb servers. WhatsAppWeb uses these messages
|
|
||||||
to built a Store, which is used to save these "secondary" information. These messages may contain
|
|
||||||
presence (available, last see) information, or just the battery status of your phone.
|
|
||||||
*/
|
|
||||||
type JsonMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleJsonMessage(message string)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The RawMessageHandler interface needs to be implemented to receive raw messages dispatched by the dispatcher.
|
|
||||||
Raw messages are the raw protobuf structs instead of the easy-to-use structs in TextMessageHandler, ImageMessageHandler, etc..
|
|
||||||
*/
|
|
||||||
type RawMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleRawMessage(message *proto.WebMessageInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The ContactListHandler interface needs to be implemented to applky custom actions to contact lists dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type ContactListHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleContactList(contacts []Contact)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The ChatListHandler interface needs to be implemented to apply custom actions to chat lists dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type ChatListHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleChatList(contacts []Chat)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The BatteryMessageHandler interface needs to be implemented to receive percentage the device connected dispatched by the dispatcher.
|
|
||||||
*/
|
|
||||||
type BatteryMessageHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleBatteryMessage(battery BatteryMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The NewContactHandler interface needs to be implemented to receive the contact's name for the first time.
|
|
||||||
*/
|
|
||||||
type NewContactHandler interface {
|
|
||||||
Handler
|
|
||||||
HandleNewContact(contact Contact)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
AddHandler adds an handler to the list of handler that receive dispatched messages.
|
|
||||||
The provided handler must at least implement the Handler interface. Additionally implemented
|
|
||||||
handlers(TextMessageHandler, ImageMessageHandler) are optional. At runtime it is checked if they are implemented
|
|
||||||
and they are called if so and needed.
|
|
||||||
*/
|
|
||||||
func (wac *Conn) AddHandler(handler Handler) {
|
|
||||||
wac.handler = append(wac.handler, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveHandler removes a handler from the list of handlers that receive dispatched messages.
|
|
||||||
func (wac *Conn) RemoveHandler(handler Handler) bool {
|
|
||||||
i := -1
|
|
||||||
for k, v := range wac.handler {
|
|
||||||
if v == handler {
|
|
||||||
i = k
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if i > -1 {
|
|
||||||
wac.handler = append(wac.handler[:i], wac.handler[i+1:]...)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveHandlers empties the list of handlers that receive dispatched messages.
|
|
||||||
func (wac *Conn) RemoveHandlers() {
|
|
||||||
wac.handler = make([]Handler, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) shouldCallSynchronously(handler Handler) bool {
|
|
||||||
sh, ok := handler.(SyncHandler)
|
|
||||||
return ok && sh.ShouldCallSynchronously()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) handle(message interface{}) {
|
|
||||||
wac.handleWithCustomHandlers(message, wac.handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) handleWithCustomHandlers(message interface{}, handlers []Handler) {
|
|
||||||
switch m := message.(type) {
|
|
||||||
case error:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
h.HandleError(m)
|
|
||||||
} else {
|
|
||||||
go h.HandleError(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(JsonMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleJsonMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleJsonMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case TextMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(TextMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleTextMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleTextMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ImageMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(ImageMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleImageMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleImageMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case VideoMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(VideoMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleVideoMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleVideoMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case AudioMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(AudioMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleAudioMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleAudioMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case DocumentMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(DocumentMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleDocumentMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleDocumentMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case LocationMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(LocationMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleLocationMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleLocationMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case LiveLocationMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(LiveLocationMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleLiveLocationMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleLiveLocationMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case StickerMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(StickerMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleStickerMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleStickerMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ContactMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(ContactMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleContactMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleContactMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case BatteryMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(BatteryMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleBatteryMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleBatteryMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case Contact:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(NewContactHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleNewContact(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleNewContact(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case ProductMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(ProductMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleProductMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleProductMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case OrderMessage:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(OrderMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleOrderMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleOrderMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case *proto.WebMessageInfo:
|
|
||||||
for _, h := range handlers {
|
|
||||||
if x, ok := h.(RawMessageHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleRawMessage(m)
|
|
||||||
} else {
|
|
||||||
go x.HandleRawMessage(m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) handleContacts(contacts interface{}) {
|
|
||||||
var contactList []Contact
|
|
||||||
c, ok := contacts.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, contact := range c {
|
|
||||||
contactNode, ok := contact.(binary.Node)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
|
||||||
contactList = append(contactList, Contact{
|
|
||||||
jid,
|
|
||||||
contactNode.Attributes["notify"],
|
|
||||||
contactNode.Attributes["name"],
|
|
||||||
contactNode.Attributes["short"],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, h := range wac.handler {
|
|
||||||
if x, ok := h.(ContactListHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleContactList(contactList)
|
|
||||||
} else {
|
|
||||||
go x.HandleContactList(contactList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) handleChats(chats interface{}) {
|
|
||||||
var chatList []Chat
|
|
||||||
c, ok := chats.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, chat := range c {
|
|
||||||
chatNode, ok := chat.(binary.Node)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
|
||||||
chatList = append(chatList, Chat{
|
|
||||||
jid,
|
|
||||||
chatNode.Attributes["name"],
|
|
||||||
chatNode.Attributes["count"],
|
|
||||||
chatNode.Attributes["t"],
|
|
||||||
chatNode.Attributes["mute"],
|
|
||||||
chatNode.Attributes["spam"],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
for _, h := range wac.handler {
|
|
||||||
if x, ok := h.(ChatListHandler); ok {
|
|
||||||
if wac.shouldCallSynchronously(h) {
|
|
||||||
x.HandleChatList(chatList)
|
|
||||||
} else {
|
|
||||||
go x.HandleChatList(chatList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) dispatch(msg interface{}) {
|
|
||||||
if msg == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch message := msg.(type) {
|
|
||||||
case *binary.Node:
|
|
||||||
if message.Description == "action" {
|
|
||||||
if con, ok := message.Content.([]interface{}); ok {
|
|
||||||
for a := range con {
|
|
||||||
if v, ok := con[a].(*proto.WebMessageInfo); ok {
|
|
||||||
wac.handle(v)
|
|
||||||
wac.handle(ParseProtoMessage(v))
|
|
||||||
}
|
|
||||||
|
|
||||||
if v, ok := con[a].(binary.Node); ok {
|
|
||||||
wac.handle(ParseNodeMessage(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if con, ok := message.Content.([]binary.Node); ok {
|
|
||||||
for a := range con {
|
|
||||||
wac.handle(ParseNodeMessage(con[a]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if message.Description == "response" && message.Attributes["type"] == "contacts" {
|
|
||||||
wac.updateContacts(message.Content)
|
|
||||||
wac.handleContacts(message.Content)
|
|
||||||
} else if message.Description == "response" && message.Attributes["type"] == "chat" {
|
|
||||||
wac.updateChats(message.Content)
|
|
||||||
wac.handleChats(message.Content)
|
|
||||||
}
|
|
||||||
case error:
|
|
||||||
wac.handle(message)
|
|
||||||
case string:
|
|
||||||
wac.handle(message)
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(os.Stderr, "unknown type in dipatcher chan: %T", msg)
|
|
||||||
}
|
|
||||||
}
|
|
221
vendor/github.com/Rhymen/go-whatsapp/media.go
generated
vendored
221
vendor/github.com/Rhymen/go-whatsapp/media.go
generated
vendored
@ -1,221 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
|
||||||
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Download(url string, mediaKey []byte, appInfo MediaType, fileLength int) ([]byte, error) {
|
|
||||||
if url == "" {
|
|
||||||
return nil, fmt.Errorf("no url present")
|
|
||||||
}
|
|
||||||
file, mac, err := downloadMedia(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
iv, cipherKey, macKey, _, err := getMediaKeys(mediaKey, appInfo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = validateMedia(iv, file, macKey, mac); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data, err := cbc.Decrypt(cipherKey, iv, file)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(data) != fileLength {
|
|
||||||
return nil, fmt.Errorf("file length does not match. Expected: %v, got: %v", fileLength, len(data))
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateMedia(iv []byte, file []byte, macKey []byte, mac []byte) error {
|
|
||||||
h := hmac.New(sha256.New, macKey)
|
|
||||||
n, err := h.Write(append(iv, file...))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if n < 10 {
|
|
||||||
return fmt.Errorf("hash to short")
|
|
||||||
}
|
|
||||||
if !hmac.Equal(h.Sum(nil)[:10], mac) {
|
|
||||||
return fmt.Errorf("invalid media hmac")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMediaKeys(mediaKey []byte, appInfo MediaType) (iv, cipherKey, macKey, refKey []byte, err error) {
|
|
||||||
mediaKeyExpanded, err := hkdf.Expand(mediaKey, 112, string(appInfo))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, nil, nil, err
|
|
||||||
}
|
|
||||||
return mediaKeyExpanded[:16], mediaKeyExpanded[16:48], mediaKeyExpanded[48:80], mediaKeyExpanded[80:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadMedia(url string) (file []byte, mac []byte, err error) {
|
|
||||||
resp, err := http.Get(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, nil, fmt.Errorf("download failed with status code %d", resp.StatusCode)
|
|
||||||
}
|
|
||||||
if resp.ContentLength <= 10 {
|
|
||||||
return nil, nil, fmt.Errorf("file to short")
|
|
||||||
}
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
n := len(data)
|
|
||||||
return data[:n-10], data[n-10 : n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type MediaConn struct {
|
|
||||||
Status int `json:"status"`
|
|
||||||
MediaConn struct {
|
|
||||||
Auth string `json:"auth"`
|
|
||||||
TTL int `json:"ttl"`
|
|
||||||
Hosts []struct {
|
|
||||||
Hostname string `json:"hostname"`
|
|
||||||
IPs []struct {
|
|
||||||
IP4 net.IP `json:"ip4"`
|
|
||||||
IP6 net.IP `json:"ip6"`
|
|
||||||
} `json:"ips"`
|
|
||||||
} `json:"hosts"`
|
|
||||||
} `json:"media_conn"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) queryMediaConn() (hostname, auth string, ttl int, err error) {
|
|
||||||
queryReq := []interface{}{"query", "mediaConn"}
|
|
||||||
ch, err := wac.writeJson(queryReq)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp MediaConn
|
|
||||||
select {
|
|
||||||
case r := <-ch:
|
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
|
||||||
return "", "", 0, fmt.Errorf("error decoding query media conn response: %v", err)
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return "", "", 0, fmt.Errorf("query media conn timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Status != http.StatusOK {
|
|
||||||
return "", "", 0, fmt.Errorf("query media conn responded with %d", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, h := range resp.MediaConn.Hosts {
|
|
||||||
if h.Hostname != "" {
|
|
||||||
return h.Hostname, resp.MediaConn.Auth, resp.MediaConn.TTL, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", "", 0, fmt.Errorf("query media conn responded with no host")
|
|
||||||
}
|
|
||||||
|
|
||||||
var mediaTypeMap = map[MediaType]string{
|
|
||||||
MediaImage: "/mms/image",
|
|
||||||
MediaVideo: "/mms/video",
|
|
||||||
MediaDocument: "/mms/document",
|
|
||||||
MediaAudio: "/mms/audio",
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (downloadURL string, mediaKey []byte, fileEncSha256 []byte, fileSha256 []byte, fileLength uint64, err error) {
|
|
||||||
data, err := ioutil.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, nil, nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaKey = make([]byte, 32)
|
|
||||||
rand.Read(mediaKey)
|
|
||||||
|
|
||||||
iv, cipherKey, macKey, _, err := getMediaKeys(mediaKey, appInfo)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, nil, nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
enc, err := cbc.Encrypt(cipherKey, iv, data)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, nil, nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fileLength = uint64(len(data))
|
|
||||||
|
|
||||||
h := hmac.New(sha256.New, macKey)
|
|
||||||
h.Write(append(iv, enc...))
|
|
||||||
mac := h.Sum(nil)[:10]
|
|
||||||
|
|
||||||
sha := sha256.New()
|
|
||||||
sha.Write(data)
|
|
||||||
fileSha256 = sha.Sum(nil)
|
|
||||||
|
|
||||||
sha.Reset()
|
|
||||||
sha.Write(append(enc, mac...))
|
|
||||||
fileEncSha256 = sha.Sum(nil)
|
|
||||||
|
|
||||||
hostname, auth, _, err := wac.queryMediaConn()
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, nil, nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
token := base64.URLEncoding.EncodeToString(fileEncSha256)
|
|
||||||
q := url.Values{
|
|
||||||
"auth": []string{auth},
|
|
||||||
"token": []string{token},
|
|
||||||
}
|
|
||||||
path := mediaTypeMap[appInfo]
|
|
||||||
uploadURL := url.URL{
|
|
||||||
Scheme: "https",
|
|
||||||
Host: hostname,
|
|
||||||
Path: fmt.Sprintf("%s/%s", path, token),
|
|
||||||
RawQuery: q.Encode(),
|
|
||||||
}
|
|
||||||
|
|
||||||
body := bytes.NewReader(append(enc, mac...))
|
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPost, uploadURL.String(), body)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, nil, nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Origin", "https://web.whatsapp.com")
|
|
||||||
req.Header.Set("Referer", "https://web.whatsapp.com/")
|
|
||||||
|
|
||||||
client := &http.Client{}
|
|
||||||
// Submit the request
|
|
||||||
res, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, nil, nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if res.StatusCode != http.StatusOK {
|
|
||||||
return "", nil, nil, nil, 0, fmt.Errorf("upload failed with status code %d", res.StatusCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
var jsonRes map[string]string
|
|
||||||
if err := json.NewDecoder(res.Body).Decode(&jsonRes); err != nil {
|
|
||||||
return "", nil, nil, nil, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonRes["url"], mediaKey, fileEncSha256, fileSha256, fileLength, nil
|
|
||||||
}
|
|
1005
vendor/github.com/Rhymen/go-whatsapp/message.go
generated
vendored
1005
vendor/github.com/Rhymen/go-whatsapp/message.go
generated
vendored
File diff suppressed because it is too large
Load Diff
65
vendor/github.com/Rhymen/go-whatsapp/profile.go
generated
vendored
65
vendor/github.com/Rhymen/go-whatsapp/profile.go
generated
vendored
@ -1,65 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pictures must be JPG 640x640 and 96x96, respectively
|
|
||||||
func (wac *Conn) UploadProfilePic(image, preview []byte) (<-chan string, error) {
|
|
||||||
tag := fmt.Sprintf("%d.--%d", time.Now().Unix(), wac.msgCount*19)
|
|
||||||
n := binary.Node{
|
|
||||||
Description: "action",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": "set",
|
|
||||||
"epoch": strconv.Itoa(wac.msgCount),
|
|
||||||
},
|
|
||||||
Content: []interface{}{
|
|
||||||
binary.Node{
|
|
||||||
Description: "picture",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"id": tag,
|
|
||||||
"jid": wac.Info.Wid,
|
|
||||||
"type": "set",
|
|
||||||
},
|
|
||||||
Content: []binary.Node{
|
|
||||||
{
|
|
||||||
Description: "image",
|
|
||||||
Attributes: nil,
|
|
||||||
Content: image,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Description: "preview",
|
|
||||||
Attributes: nil,
|
|
||||||
Content: preview,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return wac.writeBinary(n, profile, 136, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) UpdateProfileName(name string) (<-chan string, error) {
|
|
||||||
tag := fmt.Sprintf("%d.--%d", time.Now().Unix(), wac.msgCount*19)
|
|
||||||
n := binary.Node{
|
|
||||||
Description: "action",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"type": "set",
|
|
||||||
"epoch": strconv.Itoa(wac.msgCount),
|
|
||||||
},
|
|
||||||
Content: []interface{}{
|
|
||||||
binary.Node{
|
|
||||||
Description: "profile",
|
|
||||||
Attributes: map[string]string{
|
|
||||||
"name": name,
|
|
||||||
},
|
|
||||||
Content: []binary.Node{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return wac.writeBinary(n, profile, ignore, tag)
|
|
||||||
}
|
|
142
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
142
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
@ -1,142 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary"
|
|
||||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (wac *Conn) readPump() {
|
|
||||||
defer func() {
|
|
||||||
wac.wg.Done()
|
|
||||||
_, _ = wac.Disconnect()
|
|
||||||
}()
|
|
||||||
|
|
||||||
var readErr error
|
|
||||||
var msgType int
|
|
||||||
var reader io.Reader
|
|
||||||
|
|
||||||
for {
|
|
||||||
readerFound := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
if wac.ws != nil {
|
|
||||||
msgType, reader, readErr = wac.ws.conn.NextReader()
|
|
||||||
}
|
|
||||||
close(readerFound)
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-readerFound:
|
|
||||||
if readErr != nil {
|
|
||||||
wac.handle(&ErrConnectionFailed{Err: readErr})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msg, err := ioutil.ReadAll(reader)
|
|
||||||
if err != nil {
|
|
||||||
wac.handle(errors.Wrap(err, "error reading message from Reader"))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = wac.processReadData(msgType, msg)
|
|
||||||
if err != nil {
|
|
||||||
wac.handle(errors.Wrap(err, "error processing data"))
|
|
||||||
}
|
|
||||||
case <-wac.ws.close:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) processReadData(msgType int, msg []byte) error {
|
|
||||||
data := strings.SplitN(string(msg), ",", 2)
|
|
||||||
|
|
||||||
if data[0][0] == '!' { //Keep-Alive Timestamp
|
|
||||||
data = append(data, data[0][1:]) //data[1]
|
|
||||||
data[0] = "!"
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) == 2 && len(data[1]) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) != 2 || len(data[1]) == 0 {
|
|
||||||
return ErrInvalidWsData
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.listener.RLock()
|
|
||||||
listener, hasListener := wac.listener.m[data[0]]
|
|
||||||
wac.listener.RUnlock()
|
|
||||||
|
|
||||||
if hasListener {
|
|
||||||
// listener only exists for TextMessages query messages out of contact.go
|
|
||||||
// If these binary query messages can be handled another way,
|
|
||||||
// then the TextMessages, which are all JSON encoded, can directly
|
|
||||||
// be unmarshalled. The listener chan could then be changed from type
|
|
||||||
// chan string to something like chan map[string]interface{}. The unmarshalling
|
|
||||||
// in several places, especially in session.go, would then be gone.
|
|
||||||
listener <- data[1]
|
|
||||||
close(listener)
|
|
||||||
wac.removeListener(data[0])
|
|
||||||
} else if msgType == websocket.BinaryMessage {
|
|
||||||
wac.loginSessionLock.RLock()
|
|
||||||
sess := wac.session
|
|
||||||
wac.loginSessionLock.RUnlock()
|
|
||||||
if sess == nil || sess.MacKey == nil || sess.EncKey == nil {
|
|
||||||
return ErrInvalidWsState
|
|
||||||
}
|
|
||||||
message, err := wac.decryptBinaryMessage([]byte(data[1]))
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error decoding binary")
|
|
||||||
}
|
|
||||||
wac.dispatch(message)
|
|
||||||
} else { //RAW json status updates
|
|
||||||
wac.handle(string(data[1]))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
|
|
||||||
//message validation
|
|
||||||
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
|
||||||
if len(msg) < 33 {
|
|
||||||
var response struct {
|
|
||||||
Status int `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := json.Unmarshal(msg, &response); err == nil {
|
|
||||||
if response.Status == http.StatusNotFound {
|
|
||||||
return nil, ErrServerRespondedWith404
|
|
||||||
}
|
|
||||||
return nil, errors.New(fmt.Sprintf("server responded with %d", response.Status))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, ErrInvalidServerResponse
|
|
||||||
|
|
||||||
}
|
|
||||||
h2.Write([]byte(msg[32:]))
|
|
||||||
if !hmac.Equal(h2.Sum(nil), msg[:32]) {
|
|
||||||
return nil, ErrInvalidHmac
|
|
||||||
}
|
|
||||||
|
|
||||||
// message decrypt
|
|
||||||
d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "decrypting message with AES-CBC failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
// message unmarshal
|
|
||||||
message, err := binary.Unmarshal(d)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "could not decode binary")
|
|
||||||
}
|
|
||||||
|
|
||||||
return message, nil
|
|
||||||
}
|
|
532
vendor/github.com/Rhymen/go-whatsapp/session.go
generated
vendored
532
vendor/github.com/Rhymen/go-whatsapp/session.go
generated
vendored
@ -1,532 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
|
||||||
"github.com/Rhymen/go-whatsapp/crypto/curve25519"
|
|
||||||
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
|
||||||
)
|
|
||||||
|
|
||||||
//represents the WhatsAppWeb client version
|
|
||||||
var waVersion = []int{2, 2142, 12}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Session contains session individual information. To be able to resume the connection without scanning the qr code
|
|
||||||
every time you should save the Session returned by Login and use RestoreWithSession the next time you want to login.
|
|
||||||
Every successful created connection returns a new Session. The Session(ClientToken, ServerToken) is altered after
|
|
||||||
every re-login and should be saved every time.
|
|
||||||
*/
|
|
||||||
type Session struct {
|
|
||||||
ClientId string
|
|
||||||
ClientToken string
|
|
||||||
ServerToken string
|
|
||||||
EncKey []byte
|
|
||||||
MacKey []byte
|
|
||||||
Wid string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Info struct {
|
|
||||||
Battery int
|
|
||||||
Platform string
|
|
||||||
Connected bool
|
|
||||||
Pushname string
|
|
||||||
Wid string
|
|
||||||
Lc string
|
|
||||||
Phone *PhoneInfo
|
|
||||||
Plugged bool
|
|
||||||
Tos int
|
|
||||||
Lg string
|
|
||||||
Is24h bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type PhoneInfo struct {
|
|
||||||
Mcc string
|
|
||||||
Mnc string
|
|
||||||
OsVersion string
|
|
||||||
DeviceManufacturer string
|
|
||||||
DeviceModel string
|
|
||||||
OsBuildNumber string
|
|
||||||
WaVersion string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newInfoFromReq(info map[string]interface{}) *Info {
|
|
||||||
phoneInfo := info["phone"].(map[string]interface{})
|
|
||||||
|
|
||||||
ret := &Info{
|
|
||||||
Battery: int(info["battery"].(float64)),
|
|
||||||
Platform: info["platform"].(string),
|
|
||||||
Connected: info["connected"].(bool),
|
|
||||||
Pushname: info["pushname"].(string),
|
|
||||||
Wid: info["wid"].(string),
|
|
||||||
Lc: info["lc"].(string),
|
|
||||||
Phone: &PhoneInfo{
|
|
||||||
phoneInfo["mcc"].(string),
|
|
||||||
phoneInfo["mnc"].(string),
|
|
||||||
phoneInfo["os_version"].(string),
|
|
||||||
phoneInfo["device_manufacturer"].(string),
|
|
||||||
phoneInfo["device_model"].(string),
|
|
||||||
phoneInfo["os_build_number"].(string),
|
|
||||||
phoneInfo["wa_version"].(string),
|
|
||||||
},
|
|
||||||
Plugged: info["plugged"].(bool),
|
|
||||||
Lg: info["lg"].(string),
|
|
||||||
Tos: int(info["tos"].(float64)),
|
|
||||||
}
|
|
||||||
|
|
||||||
if is24h, ok := info["is24h"]; ok {
|
|
||||||
ret.Is24h = is24h.(bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
CheckCurrentServerVersion is based on the login method logic in order to establish the websocket connection and get
|
|
||||||
the current version from the server with the `admin init` command. This can be very useful for automations in which
|
|
||||||
you need to quickly perceive new versions (mostly patches) and update your application so it suddenly stops working.
|
|
||||||
*/
|
|
||||||
func CheckCurrentServerVersion() ([]int, error) {
|
|
||||||
wac, err := NewConn(5 * time.Second)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("fail to create connection")
|
|
||||||
}
|
|
||||||
|
|
||||||
clientId := make([]byte, 16)
|
|
||||||
if _, err = rand.Read(clientId); err != nil {
|
|
||||||
return nil, fmt.Errorf("error creating random ClientId: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b64ClientId := base64.StdEncoding.EncodeToString(clientId)
|
|
||||||
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, b64ClientId, true}
|
|
||||||
loginChan, err := wac.writeJson(login)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error writing login: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve an answer from the websocket
|
|
||||||
var r string
|
|
||||||
select {
|
|
||||||
case r = <-loginChan:
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return nil, fmt.Errorf("login connection timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp map[string]interface{}
|
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
|
||||||
return nil, fmt.Errorf("error decoding login: %s", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take the curr property as X.Y.Z and split it into as int slice
|
|
||||||
curr := resp["curr"].(string)
|
|
||||||
currArray := strings.Split(curr, ".")
|
|
||||||
version := make([]int, len(currArray))
|
|
||||||
for i := range version {
|
|
||||||
version[i], _ = strconv.Atoi(currArray[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
return version, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
|
|
||||||
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
|
|
||||||
*/
|
|
||||||
func (wac *Conn) SetClientName(long, short string, version string) error {
|
|
||||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
|
||||||
return fmt.Errorf("cannot change client name after logging in")
|
|
||||||
}
|
|
||||||
wac.longClientName, wac.shortClientName, wac.clientVersion = long, short, version
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
SetClientVersion sets WhatsApp client version
|
|
||||||
Default value is 0.4.2080
|
|
||||||
*/
|
|
||||||
func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
|
|
||||||
waVersion = []int{major, minor, patch}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientVersion returns WhatsApp client version
|
|
||||||
func (wac *Conn) GetClientVersion() []int {
|
|
||||||
return waVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code
|
|
||||||
every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel
|
|
||||||
as an parameter. This channel is used to push the data represented by the qr code back to the user. The received data
|
|
||||||
should be displayed as an qr code in a way you prefer. To print a qr code to console you can use:
|
|
||||||
github.com/Baozisoftware/qrcode-terminal-go Example login procedure:
|
|
||||||
wac, err := whatsapp.NewConn(5 * time.Second)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
qr := make(chan string)
|
|
||||||
go func() {
|
|
||||||
terminal := qrcodeTerminal.New()
|
|
||||||
terminal.Get(<-qr).Print()
|
|
||||||
}()
|
|
||||||
|
|
||||||
session, err := wac.Login(qr)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "error during login: %v\n", err)
|
|
||||||
}
|
|
||||||
fmt.Printf("login successful, session: %v\n", session)
|
|
||||||
*/
|
|
||||||
func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
|
||||||
session := Session{}
|
|
||||||
//Makes sure that only a single Login or Restore can happen at the same time
|
|
||||||
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
|
||||||
return session, ErrLoginInProgress
|
|
||||||
}
|
|
||||||
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
|
||||||
|
|
||||||
if wac.loggedIn {
|
|
||||||
return session, ErrAlreadyLoggedIn
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
|
||||||
return session, err
|
|
||||||
}
|
|
||||||
|
|
||||||
//logged in?!?
|
|
||||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
|
||||||
return session, fmt.Errorf("already logged in")
|
|
||||||
}
|
|
||||||
|
|
||||||
clientId := make([]byte, 16)
|
|
||||||
_, err := rand.Read(clientId)
|
|
||||||
if err != nil {
|
|
||||||
return session, fmt.Errorf("error creating random ClientId: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
session.ClientId = base64.StdEncoding.EncodeToString(clientId)
|
|
||||||
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, session.ClientId, true}
|
|
||||||
loginChan, err := wac.writeJson(login)
|
|
||||||
if err != nil {
|
|
||||||
return session, fmt.Errorf("error writing login: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var r string
|
|
||||||
select {
|
|
||||||
case r = <-loginChan:
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return session, fmt.Errorf("login connection timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
var resp map[string]interface{}
|
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
|
||||||
return session, fmt.Errorf("error decoding login resp: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var ref string
|
|
||||||
if rref, ok := resp["ref"].(string); ok {
|
|
||||||
ref = rref
|
|
||||||
} else {
|
|
||||||
return session, fmt.Errorf("error decoding login resp: invalid resp['ref']\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
priv, pub, err := curve25519.GenerateKey()
|
|
||||||
if err != nil {
|
|
||||||
return session, fmt.Errorf("error generating keys: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//listener for Login response
|
|
||||||
s1 := make(chan string, 1)
|
|
||||||
wac.listener.Lock()
|
|
||||||
wac.listener.m["s1"] = s1
|
|
||||||
wac.listener.Unlock()
|
|
||||||
|
|
||||||
qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId)
|
|
||||||
|
|
||||||
var resp2 []interface{}
|
|
||||||
select {
|
|
||||||
case r1 := <-s1:
|
|
||||||
wac.loginSessionLock.Lock()
|
|
||||||
defer wac.loginSessionLock.Unlock()
|
|
||||||
if err := json.Unmarshal([]byte(r1), &resp2); err != nil {
|
|
||||||
return session, fmt.Errorf("error decoding qr code resp: %v", err)
|
|
||||||
}
|
|
||||||
case <-time.After(time.Duration(resp["ttl"].(float64)) * time.Millisecond):
|
|
||||||
return session, fmt.Errorf("qr code scan timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
info := resp2[1].(map[string]interface{})
|
|
||||||
|
|
||||||
wac.Info = newInfoFromReq(info)
|
|
||||||
|
|
||||||
session.ClientToken = info["clientToken"].(string)
|
|
||||||
session.ServerToken = info["serverToken"].(string)
|
|
||||||
session.Wid = info["wid"].(string)
|
|
||||||
s := info["secret"].(string)
|
|
||||||
decodedSecret, err := base64.StdEncoding.DecodeString(s)
|
|
||||||
if err != nil {
|
|
||||||
return session, fmt.Errorf("error decoding secret: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubKey [32]byte
|
|
||||||
copy(pubKey[:], decodedSecret[:32])
|
|
||||||
|
|
||||||
sharedSecret := curve25519.GenerateSharedSecret(*priv, pubKey)
|
|
||||||
|
|
||||||
hash := sha256.New
|
|
||||||
|
|
||||||
nullKey := make([]byte, 32)
|
|
||||||
h := hmac.New(hash, nullKey)
|
|
||||||
h.Write(sharedSecret)
|
|
||||||
|
|
||||||
sharedSecretExtended, err := hkdf.Expand(h.Sum(nil), 80, "")
|
|
||||||
if err != nil {
|
|
||||||
return session, fmt.Errorf("hkdf error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//login validation
|
|
||||||
checkSecret := make([]byte, 112)
|
|
||||||
copy(checkSecret[:32], decodedSecret[:32])
|
|
||||||
copy(checkSecret[32:], decodedSecret[64:])
|
|
||||||
h2 := hmac.New(hash, sharedSecretExtended[32:64])
|
|
||||||
h2.Write(checkSecret)
|
|
||||||
if !hmac.Equal(h2.Sum(nil), decodedSecret[32:64]) {
|
|
||||||
return session, fmt.Errorf("abort login")
|
|
||||||
}
|
|
||||||
|
|
||||||
keysEncrypted := make([]byte, 96)
|
|
||||||
copy(keysEncrypted[:16], sharedSecretExtended[64:])
|
|
||||||
copy(keysEncrypted[16:], decodedSecret[64:])
|
|
||||||
|
|
||||||
keyDecrypted, err := cbc.Decrypt(sharedSecretExtended[:32], nil, keysEncrypted)
|
|
||||||
if err != nil {
|
|
||||||
return session, fmt.Errorf("error decryptAes: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
session.EncKey = keyDecrypted[:32]
|
|
||||||
session.MacKey = keyDecrypted[32:64]
|
|
||||||
wac.session = &session
|
|
||||||
wac.loggedIn = true
|
|
||||||
|
|
||||||
return session, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: GoDoc
|
|
||||||
/*
|
|
||||||
Basically the old RestoreSession functionality
|
|
||||||
*/
|
|
||||||
func (wac *Conn) RestoreWithSession(session Session) (_ Session, err error) {
|
|
||||||
if wac.loggedIn {
|
|
||||||
return Session{}, ErrAlreadyLoggedIn
|
|
||||||
}
|
|
||||||
old := wac.session
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
wac.session = old
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
wac.session = &session
|
|
||||||
|
|
||||||
if err = wac.Restore(); err != nil {
|
|
||||||
wac.session = nil
|
|
||||||
return Session{}, err
|
|
||||||
}
|
|
||||||
return *wac.session, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//TODO: GoDoc
|
|
||||||
RestoreWithSession is the function that restores a given session. It will try to reestablish the connection to the
|
|
||||||
WhatsAppWeb servers with the provided session. If it succeeds it will return a new session. This new session has to be
|
|
||||||
saved because the Client and Server-Token will change after every login. Logging in with old tokens is possible, but not
|
|
||||||
suggested. If so, a challenge has to be resolved which is just another possible point of failure.
|
|
||||||
*/
|
|
||||||
func (wac *Conn) Restore() error {
|
|
||||||
//Makes sure that only a single Login or Restore can happen at the same time
|
|
||||||
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
|
||||||
return ErrLoginInProgress
|
|
||||||
}
|
|
||||||
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
|
||||||
|
|
||||||
if wac.session == nil {
|
|
||||||
return ErrInvalidSession
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if wac.loggedIn {
|
|
||||||
return ErrAlreadyLoggedIn
|
|
||||||
}
|
|
||||||
|
|
||||||
//listener for Conn or challenge; s1 is not allowed to drop
|
|
||||||
s1 := make(chan string, 1)
|
|
||||||
wac.listener.Lock()
|
|
||||||
wac.listener.m["s1"] = s1
|
|
||||||
wac.listener.Unlock()
|
|
||||||
|
|
||||||
//admin init
|
|
||||||
init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName, wac.clientVersion}, wac.session.ClientId, true}
|
|
||||||
initChan, err := wac.writeJson(init)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error writing admin init: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//admin login with takeover
|
|
||||||
login := []interface{}{"admin", "login", wac.session.ClientToken, wac.session.ServerToken, wac.session.ClientId, "takeover"}
|
|
||||||
loginChan, err := wac.writeJson(login)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error writing admin login: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case r := <-initChan:
|
|
||||||
var resp map[string]interface{}
|
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
|
||||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if int(resp["status"].(float64)) != 200 {
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("init responded with %d", resp["status"])
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("restore session init timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
//wait for s1
|
|
||||||
var connResp []interface{}
|
|
||||||
select {
|
|
||||||
case r1 := <-s1:
|
|
||||||
if err := json.Unmarshal([]byte(r1), &connResp); err != nil {
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("error decoding s1 message: %v\n", err)
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
wac.timeTag = ""
|
|
||||||
//check for an error message
|
|
||||||
select {
|
|
||||||
case r := <-loginChan:
|
|
||||||
var resp map[string]interface{}
|
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
|
||||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
|
||||||
}
|
|
||||||
if int(resp["status"].(float64)) != 200 {
|
|
||||||
return fmt.Errorf("admin login responded with %d", int(resp["status"].(float64)))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// not even an error message – assume timeout
|
|
||||||
return fmt.Errorf("restore session connection timed out")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//check if challenge is present
|
|
||||||
if len(connResp) == 2 && connResp[0] == "Cmd" && connResp[1].(map[string]interface{})["type"] == "challenge" {
|
|
||||||
s2 := make(chan string, 1)
|
|
||||||
wac.listener.Lock()
|
|
||||||
wac.listener.m["s2"] = s2
|
|
||||||
wac.listener.Unlock()
|
|
||||||
|
|
||||||
if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil {
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("error resolving challenge: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case r := <-s2:
|
|
||||||
if err := json.Unmarshal([]byte(r), &connResp); err != nil {
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("error decoding s2 message: %v\n", err)
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("restore session challenge timed out")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//check for login 200 --> login success
|
|
||||||
select {
|
|
||||||
case r := <-loginChan:
|
|
||||||
var resp map[string]interface{}
|
|
||||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if int(resp["status"].(float64)) != 200 {
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("admin login responded with %d", resp["status"])
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
wac.timeTag = ""
|
|
||||||
return fmt.Errorf("restore session login timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
info := connResp[1].(map[string]interface{})
|
|
||||||
|
|
||||||
wac.Info = newInfoFromReq(info)
|
|
||||||
|
|
||||||
//set new tokens
|
|
||||||
wac.session.ClientToken = info["clientToken"].(string)
|
|
||||||
wac.session.ServerToken = info["serverToken"].(string)
|
|
||||||
wac.session.Wid = info["wid"].(string)
|
|
||||||
wac.loggedIn = true
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) resolveChallenge(challenge string) error {
|
|
||||||
decoded, err := base64.StdEncoding.DecodeString(challenge)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
|
||||||
h2.Write([]byte(decoded))
|
|
||||||
|
|
||||||
ch := []interface{}{"admin", "challenge", base64.StdEncoding.EncodeToString(h2.Sum(nil)), wac.session.ServerToken, wac.session.ClientId}
|
|
||||||
challengeChan, err := wac.writeJson(ch)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error writing challenge: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case r := <-challengeChan:
|
|
||||||
var resp map[string]interface{}
|
|
||||||
if err := json.Unmarshal([]byte(r), &resp); err != nil {
|
|
||||||
return fmt.Errorf("error decoding login resp: %v\n", err)
|
|
||||||
}
|
|
||||||
if int(resp["status"].(float64)) != 200 {
|
|
||||||
return fmt.Errorf("challenge responded with %d\n", resp["status"])
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return fmt.Errorf("connection timed out")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Logout is the function to logout from a WhatsApp session. Logging out means invalidating the current session.
|
|
||||||
The session can not be resumed and will disappear on your phone in the WhatsAppWeb client list.
|
|
||||||
*/
|
|
||||||
func (wac *Conn) Logout() error {
|
|
||||||
login := []interface{}{"admin", "Conn", "disconnect"}
|
|
||||||
_, err := wac.writeJson(login)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error writing logout: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.loggedIn = false
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
@ -1,80 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Store struct {
|
|
||||||
Contacts map[string]Contact
|
|
||||||
Chats map[string]Chat
|
|
||||||
}
|
|
||||||
|
|
||||||
type Contact struct {
|
|
||||||
Jid string
|
|
||||||
Notify string
|
|
||||||
Name string
|
|
||||||
Short string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Chat struct {
|
|
||||||
Jid string
|
|
||||||
Name string
|
|
||||||
Unread string
|
|
||||||
LastMessageTime string
|
|
||||||
IsMuted string
|
|
||||||
IsMarkedSpam string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStore() *Store {
|
|
||||||
return &Store{
|
|
||||||
make(map[string]Contact),
|
|
||||||
make(map[string]Chat),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) updateContacts(contacts interface{}) {
|
|
||||||
c, ok := contacts.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, contact := range c {
|
|
||||||
contactNode, ok := contact.(binary.Node)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
|
||||||
wac.Store.Contacts[jid] = Contact{
|
|
||||||
jid,
|
|
||||||
contactNode.Attributes["notify"],
|
|
||||||
contactNode.Attributes["name"],
|
|
||||||
contactNode.Attributes["short"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) updateChats(chats interface{}) {
|
|
||||||
c, ok := chats.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, chat := range c {
|
|
||||||
chatNode, ok := chat.(binary.Node)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
|
||||||
wac.Store.Chats[jid] = Chat{
|
|
||||||
jid,
|
|
||||||
chatNode.Attributes["name"],
|
|
||||||
chatNode.Attributes["count"],
|
|
||||||
chatNode.Attributes["t"],
|
|
||||||
chatNode.Attributes["mute"],
|
|
||||||
chatNode.Attributes["spam"],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
195
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
195
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
@ -1,195 +0,0 @@
|
|||||||
package whatsapp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp/binary"
|
|
||||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (wac *Conn) addListener(ch chan string, messageTag string) {
|
|
||||||
wac.listener.Lock()
|
|
||||||
wac.listener.m[messageTag] = ch
|
|
||||||
wac.listener.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) removeListener(answerMessageTag string) {
|
|
||||||
wac.listener.Lock()
|
|
||||||
delete(wac.listener.m, answerMessageTag)
|
|
||||||
wac.listener.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
//writeJson enqueues a json message into the writeChan
|
|
||||||
func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) {
|
|
||||||
|
|
||||||
ch := make(chan string, 1)
|
|
||||||
|
|
||||||
wac.writerLock.Lock()
|
|
||||||
defer wac.writerLock.Unlock()
|
|
||||||
|
|
||||||
d, err := json.Marshal(data)
|
|
||||||
if err != nil {
|
|
||||||
close(ch)
|
|
||||||
return ch, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ts := time.Now().Unix()
|
|
||||||
messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
|
||||||
bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d))
|
|
||||||
|
|
||||||
if wac.timeTag == "" {
|
|
||||||
tss := fmt.Sprintf("%d", ts)
|
|
||||||
wac.timeTag = tss[len(tss)-3:]
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.addListener(ch, messageTag)
|
|
||||||
|
|
||||||
err = wac.write(websocket.TextMessage, bytes)
|
|
||||||
if err != nil {
|
|
||||||
close(ch)
|
|
||||||
wac.removeListener(messageTag)
|
|
||||||
return ch, err
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.msgCount++
|
|
||||||
return ch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) {
|
|
||||||
|
|
||||||
ch := make(chan string, 1)
|
|
||||||
|
|
||||||
if len(messageTag) < 2 {
|
|
||||||
close(ch)
|
|
||||||
return ch, ErrMissingMessageTag
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.writerLock.Lock()
|
|
||||||
defer wac.writerLock.Unlock()
|
|
||||||
|
|
||||||
data, err := wac.encryptBinaryMessage(node)
|
|
||||||
if err != nil {
|
|
||||||
close(ch)
|
|
||||||
return ch, errors.Wrap(err, "encryptBinaryMessage(node) failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes := []byte(messageTag + ",")
|
|
||||||
bytes = append(bytes, byte(metric), byte(flag))
|
|
||||||
bytes = append(bytes, data...)
|
|
||||||
|
|
||||||
wac.addListener(ch, messageTag)
|
|
||||||
|
|
||||||
err = wac.write(websocket.BinaryMessage, bytes)
|
|
||||||
if err != nil {
|
|
||||||
close(ch)
|
|
||||||
wac.removeListener(messageTag)
|
|
||||||
return ch, errors.Wrap(err, "failed to write message")
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.msgCount++
|
|
||||||
return ch, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) sendKeepAlive() error {
|
|
||||||
|
|
||||||
respChan := make(chan string, 1)
|
|
||||||
wac.addListener(respChan, "!")
|
|
||||||
|
|
||||||
bytes := []byte("?,,")
|
|
||||||
err := wac.write(websocket.TextMessage, bytes)
|
|
||||||
if err != nil {
|
|
||||||
close(respChan)
|
|
||||||
wac.removeListener("!")
|
|
||||||
return errors.Wrap(err, "error sending keepAlive")
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case resp := <-respChan:
|
|
||||||
msecs, err := strconv.ParseInt(resp, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Error converting time string to uint")
|
|
||||||
}
|
|
||||||
wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond))
|
|
||||||
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return ErrConnectionTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
When phone is unreachable, WhatsAppWeb sends ["admin","test"] time after time to try a successful contact.
|
|
||||||
Tested with Airplane mode and no connection at all.
|
|
||||||
*/
|
|
||||||
func (wac *Conn) sendAdminTest() (bool, error) {
|
|
||||||
data := []interface{}{"admin", "test"}
|
|
||||||
|
|
||||||
r, err := wac.writeJson(data)
|
|
||||||
if err != nil {
|
|
||||||
return false, errors.Wrap(err, "error sending admin test")
|
|
||||||
}
|
|
||||||
|
|
||||||
var response []interface{}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case resp := <-r:
|
|
||||||
if err := json.Unmarshal([]byte(resp), &response); err != nil {
|
|
||||||
return false, fmt.Errorf("error decoding response message: %v\n", err)
|
|
||||||
}
|
|
||||||
case <-time.After(wac.msgTimeout):
|
|
||||||
return false, ErrConnectionTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(response) == 2 && response[0].(string) == "Pong" && response[1].(bool) == true {
|
|
||||||
return true, nil
|
|
||||||
} else {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) write(messageType int, data []byte) error {
|
|
||||||
|
|
||||||
if wac == nil || wac.ws == nil {
|
|
||||||
return ErrInvalidWebsocket
|
|
||||||
}
|
|
||||||
|
|
||||||
wac.ws.Lock()
|
|
||||||
err := wac.ws.conn.WriteMessage(messageType, data)
|
|
||||||
wac.ws.Unlock()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "error writing to websocket")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) {
|
|
||||||
b, err := binary.Marshal(node)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "binary node marshal failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "encrypt failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
h := hmac.New(sha256.New, wac.session.MacKey)
|
|
||||||
h.Write(cipher)
|
|
||||||
hash := h.Sum(nil)
|
|
||||||
|
|
||||||
data = append(data, hash[:32]...)
|
|
||||||
data = append(data, cipher...)
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
19
vendor/github.com/kballard/go-shellquote/LICENSE
generated
vendored
Normal file
19
vendor/github.com/kballard/go-shellquote/LICENSE
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2014 Kevin Ballard
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||||
|
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||||
|
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
36
vendor/github.com/kballard/go-shellquote/README
generated
vendored
Normal file
36
vendor/github.com/kballard/go-shellquote/README
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
PACKAGE
|
||||||
|
|
||||||
|
package shellquote
|
||||||
|
import "github.com/kballard/go-shellquote"
|
||||||
|
|
||||||
|
Shellquote provides utilities for joining/splitting strings using sh's
|
||||||
|
word-splitting rules.
|
||||||
|
|
||||||
|
VARIABLES
|
||||||
|
|
||||||
|
var (
|
||||||
|
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
|
||||||
|
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
|
||||||
|
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
FUNCTIONS
|
||||||
|
|
||||||
|
func Join(args ...string) string
|
||||||
|
Join quotes each argument and joins them with a space. If passed to
|
||||||
|
/bin/sh, the resulting string will be split back into the original
|
||||||
|
arguments.
|
||||||
|
|
||||||
|
func Split(input string) (words []string, err error)
|
||||||
|
Split splits a string according to /bin/sh's word-splitting rules. It
|
||||||
|
supports backslash-escapes, single-quotes, and double-quotes. Notably it
|
||||||
|
does not support the $'' style of quoting. It also doesn't attempt to
|
||||||
|
perform any other sort of expansion, including brace expansion, shell
|
||||||
|
expansion, or pathname expansion.
|
||||||
|
|
||||||
|
If the given input has an unterminated quoted string or ends in a
|
||||||
|
backslash-escape, one of UnterminatedSingleQuoteError,
|
||||||
|
UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
|
||||||
|
|
||||||
|
|
3
vendor/github.com/kballard/go-shellquote/doc.go
generated
vendored
Normal file
3
vendor/github.com/kballard/go-shellquote/doc.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Shellquote provides utilities for joining/splitting strings using sh's
|
||||||
|
// word-splitting rules.
|
||||||
|
package shellquote
|
102
vendor/github.com/kballard/go-shellquote/quote.go
generated
vendored
Normal file
102
vendor/github.com/kballard/go-shellquote/quote.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package shellquote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Join quotes each argument and joins them with a space.
|
||||||
|
// If passed to /bin/sh, the resulting string will be split back into the
|
||||||
|
// original arguments.
|
||||||
|
func Join(args ...string) string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, arg := range args {
|
||||||
|
if i != 0 {
|
||||||
|
buf.WriteByte(' ')
|
||||||
|
}
|
||||||
|
quote(arg, &buf)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
specialChars = "\\'\"`${[|&;<>()*?!"
|
||||||
|
extraSpecialChars = " \t\n"
|
||||||
|
prefixChars = "~"
|
||||||
|
)
|
||||||
|
|
||||||
|
func quote(word string, buf *bytes.Buffer) {
|
||||||
|
// We want to try to produce a "nice" output. As such, we will
|
||||||
|
// backslash-escape most characters, but if we encounter a space, or if we
|
||||||
|
// encounter an extra-special char (which doesn't work with
|
||||||
|
// backslash-escaping) we switch over to quoting the whole word. We do this
|
||||||
|
// with a space because it's typically easier for people to read multi-word
|
||||||
|
// arguments when quoted with a space rather than with ugly backslashes
|
||||||
|
// everywhere.
|
||||||
|
origLen := buf.Len()
|
||||||
|
|
||||||
|
if len(word) == 0 {
|
||||||
|
// oops, no content
|
||||||
|
buf.WriteString("''")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cur, prev := word, word
|
||||||
|
atStart := true
|
||||||
|
for len(cur) > 0 {
|
||||||
|
c, l := utf8.DecodeRuneInString(cur)
|
||||||
|
cur = cur[l:]
|
||||||
|
if strings.ContainsRune(specialChars, c) || (atStart && strings.ContainsRune(prefixChars, c)) {
|
||||||
|
// copy the non-special chars up to this point
|
||||||
|
if len(cur) < len(prev) {
|
||||||
|
buf.WriteString(prev[0 : len(prev)-len(cur)-l])
|
||||||
|
}
|
||||||
|
buf.WriteByte('\\')
|
||||||
|
buf.WriteRune(c)
|
||||||
|
prev = cur
|
||||||
|
} else if strings.ContainsRune(extraSpecialChars, c) {
|
||||||
|
// start over in quote mode
|
||||||
|
buf.Truncate(origLen)
|
||||||
|
goto quote
|
||||||
|
}
|
||||||
|
atStart = false
|
||||||
|
}
|
||||||
|
if len(prev) > 0 {
|
||||||
|
buf.WriteString(prev)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
quote:
|
||||||
|
// quote mode
|
||||||
|
// Use single-quotes, but if we find a single-quote in the word, we need
|
||||||
|
// to terminate the string, emit an escaped quote, and start the string up
|
||||||
|
// again
|
||||||
|
inQuote := false
|
||||||
|
for len(word) > 0 {
|
||||||
|
i := strings.IndexRune(word, '\'')
|
||||||
|
if i == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
if !inQuote {
|
||||||
|
buf.WriteByte('\'')
|
||||||
|
inQuote = true
|
||||||
|
}
|
||||||
|
buf.WriteString(word[0:i])
|
||||||
|
}
|
||||||
|
word = word[i+1:]
|
||||||
|
if inQuote {
|
||||||
|
buf.WriteByte('\'')
|
||||||
|
inQuote = false
|
||||||
|
}
|
||||||
|
buf.WriteString("\\'")
|
||||||
|
}
|
||||||
|
if len(word) > 0 {
|
||||||
|
if !inQuote {
|
||||||
|
buf.WriteByte('\'')
|
||||||
|
}
|
||||||
|
buf.WriteString(word)
|
||||||
|
buf.WriteByte('\'')
|
||||||
|
}
|
||||||
|
}
|
156
vendor/github.com/kballard/go-shellquote/unquote.go
generated
vendored
Normal file
156
vendor/github.com/kballard/go-shellquote/unquote.go
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package shellquote
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
|
||||||
|
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
|
||||||
|
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
splitChars = " \n\t"
|
||||||
|
singleChar = '\''
|
||||||
|
doubleChar = '"'
|
||||||
|
escapeChar = '\\'
|
||||||
|
doubleEscapeChars = "$`\"\n\\"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Split splits a string according to /bin/sh's word-splitting rules. It
|
||||||
|
// supports backslash-escapes, single-quotes, and double-quotes. Notably it does
|
||||||
|
// not support the $'' style of quoting. It also doesn't attempt to perform any
|
||||||
|
// other sort of expansion, including brace expansion, shell expansion, or
|
||||||
|
// pathname expansion.
|
||||||
|
//
|
||||||
|
// If the given input has an unterminated quoted string or ends in a
|
||||||
|
// backslash-escape, one of UnterminatedSingleQuoteError,
|
||||||
|
// UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
|
||||||
|
func Split(input string) (words []string, err error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
words = make([]string, 0)
|
||||||
|
|
||||||
|
for len(input) > 0 {
|
||||||
|
// skip any splitChars at the start
|
||||||
|
c, l := utf8.DecodeRuneInString(input)
|
||||||
|
if strings.ContainsRune(splitChars, c) {
|
||||||
|
input = input[l:]
|
||||||
|
continue
|
||||||
|
} else if c == escapeChar {
|
||||||
|
// Look ahead for escaped newline so we can skip over it
|
||||||
|
next := input[l:]
|
||||||
|
if len(next) == 0 {
|
||||||
|
err = UnterminatedEscapeError
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c2, l2 := utf8.DecodeRuneInString(next)
|
||||||
|
if c2 == '\n' {
|
||||||
|
input = next[l2:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var word string
|
||||||
|
word, input, err = splitWord(input, &buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
words = append(words, word)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
|
||||||
|
buf.Reset()
|
||||||
|
|
||||||
|
raw:
|
||||||
|
{
|
||||||
|
cur := input
|
||||||
|
for len(cur) > 0 {
|
||||||
|
c, l := utf8.DecodeRuneInString(cur)
|
||||||
|
cur = cur[l:]
|
||||||
|
if c == singleChar {
|
||||||
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||||
|
input = cur
|
||||||
|
goto single
|
||||||
|
} else if c == doubleChar {
|
||||||
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||||
|
input = cur
|
||||||
|
goto double
|
||||||
|
} else if c == escapeChar {
|
||||||
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||||
|
input = cur
|
||||||
|
goto escape
|
||||||
|
} else if strings.ContainsRune(splitChars, c) {
|
||||||
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||||
|
return buf.String(), cur, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(input) > 0 {
|
||||||
|
buf.WriteString(input)
|
||||||
|
input = ""
|
||||||
|
}
|
||||||
|
goto done
|
||||||
|
}
|
||||||
|
|
||||||
|
escape:
|
||||||
|
{
|
||||||
|
if len(input) == 0 {
|
||||||
|
return "", "", UnterminatedEscapeError
|
||||||
|
}
|
||||||
|
c, l := utf8.DecodeRuneInString(input)
|
||||||
|
if c == '\n' {
|
||||||
|
// a backslash-escaped newline is elided from the output entirely
|
||||||
|
} else {
|
||||||
|
buf.WriteString(input[:l])
|
||||||
|
}
|
||||||
|
input = input[l:]
|
||||||
|
}
|
||||||
|
goto raw
|
||||||
|
|
||||||
|
single:
|
||||||
|
{
|
||||||
|
i := strings.IndexRune(input, singleChar)
|
||||||
|
if i == -1 {
|
||||||
|
return "", "", UnterminatedSingleQuoteError
|
||||||
|
}
|
||||||
|
buf.WriteString(input[0:i])
|
||||||
|
input = input[i+1:]
|
||||||
|
goto raw
|
||||||
|
}
|
||||||
|
|
||||||
|
double:
|
||||||
|
{
|
||||||
|
cur := input
|
||||||
|
for len(cur) > 0 {
|
||||||
|
c, l := utf8.DecodeRuneInString(cur)
|
||||||
|
cur = cur[l:]
|
||||||
|
if c == doubleChar {
|
||||||
|
buf.WriteString(input[0 : len(input)-len(cur)-l])
|
||||||
|
input = cur
|
||||||
|
goto raw
|
||||||
|
} else if c == escapeChar {
|
||||||
|
// bash only supports certain escapes in double-quoted strings
|
||||||
|
c2, l2 := utf8.DecodeRuneInString(cur)
|
||||||
|
cur = cur[l2:]
|
||||||
|
if strings.ContainsRune(doubleEscapeChars, c2) {
|
||||||
|
buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
|
||||||
|
if c2 == '\n' {
|
||||||
|
// newline is special, skip the backslash entirely
|
||||||
|
} else {
|
||||||
|
buf.WriteRune(c2)
|
||||||
|
}
|
||||||
|
input = cur
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", "", UnterminatedDoubleQuoteError
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
return buf.String(), input, nil
|
||||||
|
}
|
4
vendor/github.com/mdp/qrterminal/.travis.yml
generated
vendored
Normal file
4
vendor/github.com/mdp/qrterminal/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- tip
|
11
vendor/github.com/mdp/qrterminal/CHANGELOG.md
generated
vendored
Normal file
11
vendor/github.com/mdp/qrterminal/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
Update to add a quiet zone border to the QR Code - #5 and fixed by [WindomZ](https://github.com/WindomZ) #8
|
||||||
|
|
||||||
|
- This can be configured with the `QuietZone int` option
|
||||||
|
- Defaults to 4 'pixels' wide to match the QR Code spec
|
||||||
|
- This alters the size of the barcode considerably and is therefore a breaking change, resulting in a bump to v1.0.0
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
Fix direction of the qr code #6 by (https://github.com/mattn)
|
78
vendor/github.com/mdp/qrterminal/README.md
generated
vendored
Normal file
78
vendor/github.com/mdp/qrterminal/README.md
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# QRCode Terminal
|
||||||
|
|
||||||
|
[![Build Status](https://secure.travis-ci.org/mdp/qrterminal.png)](https://travis-ci.org/mdp/qrterminal)
|
||||||
|
|
||||||
|
A golang library for generating QR codes in the terminal.
|
||||||
|
|
||||||
|
Originally this was a port of the [NodeJS version](https://github.com/gtanner/qrcode-terminal). Recently it's been updated to allow for smaller code generation using ASCII 'half blocks'
|
||||||
|
|
||||||
|
## Example
|
||||||
|
Full size ASCII block QR Code:
|
||||||
|
<img src="https://user-images.githubusercontent.com/2868/37992336-0ba06b56-31d1-11e8-9d32-5c6bb008dc74.png" alt="alt text" width="225" height="225">
|
||||||
|
|
||||||
|
Smaller 'half blocks' in the terminal:
|
||||||
|
<img src="https://user-images.githubusercontent.com/2868/37992371-243d4238-31d1-11e8-92f8-e34a794b21af.png" alt="alt text" width="225" height="225">
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
`go get github.com/mdp/qrterminal`
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/mdp/qrterminal"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Generate a 'dense' qrcode with the 'Low' level error correction and write it to Stdout
|
||||||
|
qrterminal.Generate("https://github.com/mdp/qrterminal", qrterminal.L, os.Stdout)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### More complicated
|
||||||
|
|
||||||
|
Large Inverted barcode with medium redundancy and a 1 pixel border
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/mdp/qrterminal"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config := qrterminal.Config{
|
||||||
|
Level: qrterminal.M,
|
||||||
|
Writer: os.Stdout,
|
||||||
|
BlackChar: qrterminal.WHITE,
|
||||||
|
WhiteChar: qrterminal.BLACK,
|
||||||
|
QuietZone: 1,
|
||||||
|
}
|
||||||
|
qrterminal.GenerateWithConfig("https://github.com/mdp/qrterminal", config)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
HalfBlock barcode with medium redundancy
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/mdp/qrterminal"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
config := qrterminal.Config{
|
||||||
|
HalfBlocks: true,
|
||||||
|
Level: qrterminal.M,
|
||||||
|
Writer: os.Stdout,
|
||||||
|
}
|
||||||
|
qrterminal.Generate("https://github.com/mdp/qrterminal", config)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Credits:
|
||||||
|
|
||||||
|
Mark Percival m@mdp.im
|
||||||
|
[Matthew Kennerly](https://github.com/mtkennerly)
|
||||||
|
[Viric](https://github.com/viric)
|
||||||
|
[WindomZ](https://github.com/WindomZ)
|
||||||
|
[mattn](https://github.com/mattn)
|
153
vendor/github.com/mdp/qrterminal/qrterminal.go
generated
vendored
Normal file
153
vendor/github.com/mdp/qrterminal/qrterminal.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package qrterminal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"rsc.io/qr"
|
||||||
|
)
|
||||||
|
|
||||||
|
const WHITE = "\033[47m \033[0m"
|
||||||
|
const BLACK = "\033[40m \033[0m"
|
||||||
|
|
||||||
|
// Use ascii blocks to form the QR Code
|
||||||
|
const BLACK_WHITE = "▄"
|
||||||
|
const BLACK_BLACK = " "
|
||||||
|
const WHITE_BLACK = "▀"
|
||||||
|
const WHITE_WHITE = "█"
|
||||||
|
|
||||||
|
// Level - the QR Code's redundancy level
|
||||||
|
const H = qr.H
|
||||||
|
const M = qr.M
|
||||||
|
const L = qr.L
|
||||||
|
|
||||||
|
// default is 4-pixel-wide white quiet zone
|
||||||
|
const QUIET_ZONE = 4
|
||||||
|
|
||||||
|
//Config for generating a barcode
|
||||||
|
type Config struct {
|
||||||
|
Level qr.Level
|
||||||
|
Writer io.Writer
|
||||||
|
HalfBlocks bool
|
||||||
|
BlackChar string
|
||||||
|
BlackWhiteChar string
|
||||||
|
WhiteChar string
|
||||||
|
WhiteBlackChar string
|
||||||
|
QuietZone int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) writeFullBlocks(w io.Writer, code *qr.Code) {
|
||||||
|
white := c.WhiteChar
|
||||||
|
black := c.BlackChar
|
||||||
|
|
||||||
|
// Frame the barcode in a 1 pixel border
|
||||||
|
w.Write([]byte(stringRepeat(stringRepeat(white,
|
||||||
|
code.Size+c.QuietZone*2)+"\n", c.QuietZone))) // top border
|
||||||
|
for i := 0; i <= code.Size; i++ {
|
||||||
|
w.Write([]byte(stringRepeat(white, c.QuietZone))) // left border
|
||||||
|
for j := 0; j <= code.Size; j++ {
|
||||||
|
if code.Black(j, i) {
|
||||||
|
w.Write([]byte(black))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(white))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Write([]byte(stringRepeat(white, c.QuietZone-1) + "\n")) // right border
|
||||||
|
}
|
||||||
|
w.Write([]byte(stringRepeat(stringRepeat(white,
|
||||||
|
code.Size+c.QuietZone*2)+"\n", c.QuietZone-1))) // bottom border
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) writeHalfBlocks(w io.Writer, code *qr.Code) {
|
||||||
|
ww := c.WhiteChar
|
||||||
|
bb := c.BlackChar
|
||||||
|
wb := c.WhiteBlackChar
|
||||||
|
bw := c.BlackWhiteChar
|
||||||
|
// Frame the barcode in a 4 pixel border
|
||||||
|
// top border
|
||||||
|
if c.QuietZone%2 != 0 {
|
||||||
|
w.Write([]byte(stringRepeat(bw, code.Size+c.QuietZone*2) + "\n"))
|
||||||
|
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||||
|
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2)))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||||
|
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2)))
|
||||||
|
}
|
||||||
|
for i := 0; i <= code.Size; i += 2 {
|
||||||
|
w.Write([]byte(stringRepeat(ww, c.QuietZone))) // left border
|
||||||
|
for j := 0; j <= code.Size; j++ {
|
||||||
|
next_black := false
|
||||||
|
if i+1 < code.Size {
|
||||||
|
next_black = code.Black(j, i+1)
|
||||||
|
}
|
||||||
|
curr_black := code.Black(j, i)
|
||||||
|
if curr_black && next_black {
|
||||||
|
w.Write([]byte(bb))
|
||||||
|
} else if curr_black && !next_black {
|
||||||
|
w.Write([]byte(bw))
|
||||||
|
} else if !curr_black && !next_black {
|
||||||
|
w.Write([]byte(ww))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(wb))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Write([]byte(stringRepeat(ww, c.QuietZone-1) + "\n")) // right border
|
||||||
|
}
|
||||||
|
// bottom border
|
||||||
|
if c.QuietZone%2 == 0 {
|
||||||
|
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||||
|
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2-1)))
|
||||||
|
w.Write([]byte(stringRepeat(wb, code.Size+c.QuietZone*2) + "\n"))
|
||||||
|
} else {
|
||||||
|
w.Write([]byte(stringRepeat(stringRepeat(ww,
|
||||||
|
code.Size+c.QuietZone*2)+"\n", c.QuietZone/2)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringRepeat(s string, count int) string {
|
||||||
|
if count <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.Repeat(s, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateWithConfig expects a string to encode and a config
|
||||||
|
func GenerateWithConfig(text string, config Config) {
|
||||||
|
if config.QuietZone < 1 {
|
||||||
|
config.QuietZone = 1 // at least 1-pixel-wide white quiet zone
|
||||||
|
}
|
||||||
|
w := config.Writer
|
||||||
|
code, _ := qr.Encode(text, config.Level)
|
||||||
|
if config.HalfBlocks {
|
||||||
|
config.writeHalfBlocks(w, code)
|
||||||
|
} else {
|
||||||
|
config.writeFullBlocks(w, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a QR Code and write it out to io.Writer
|
||||||
|
func Generate(text string, l qr.Level, w io.Writer) {
|
||||||
|
config := Config{
|
||||||
|
Level: l,
|
||||||
|
Writer: w,
|
||||||
|
BlackChar: BLACK,
|
||||||
|
WhiteChar: WHITE,
|
||||||
|
QuietZone: QUIET_ZONE,
|
||||||
|
}
|
||||||
|
GenerateWithConfig(text, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a QR Code with half blocks and write it out to io.Writer
|
||||||
|
func GenerateHalfBlock(text string, l qr.Level, w io.Writer) {
|
||||||
|
config := Config{
|
||||||
|
Level: l,
|
||||||
|
Writer: w,
|
||||||
|
HalfBlocks: true,
|
||||||
|
BlackChar: BLACK_BLACK,
|
||||||
|
WhiteBlackChar: WHITE_BLACK,
|
||||||
|
WhiteChar: WHITE_WHITE,
|
||||||
|
BlackWhiteChar: BLACK_WHITE,
|
||||||
|
QuietZone: QUIET_ZONE,
|
||||||
|
}
|
||||||
|
GenerateWithConfig(text, config)
|
||||||
|
}
|
27
vendor/github.com/remyoudompheng/bigfft/LICENSE
generated
vendored
Normal file
27
vendor/github.com/remyoudompheng/bigfft/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
43
vendor/github.com/remyoudompheng/bigfft/README
generated
vendored
Normal file
43
vendor/github.com/remyoudompheng/bigfft/README
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
Benchmarking math/big vs. bigfft
|
||||||
|
|
||||||
|
Number size old ns/op new ns/op delta
|
||||||
|
1kb 1599 1640 +2.56%
|
||||||
|
10kb 61533 62170 +1.04%
|
||||||
|
50kb 833693 831051 -0.32%
|
||||||
|
100kb 2567995 2693864 +4.90%
|
||||||
|
1Mb 105237800 28446400 -72.97%
|
||||||
|
5Mb 1272947000 168554600 -86.76%
|
||||||
|
10Mb 3834354000 405120200 -89.43%
|
||||||
|
20Mb 11514488000 845081600 -92.66%
|
||||||
|
50Mb 49199945000 2893950000 -94.12%
|
||||||
|
100Mb 147599836000 5921594000 -95.99%
|
||||||
|
|
||||||
|
Benchmarking GMP vs bigfft
|
||||||
|
|
||||||
|
Number size GMP ns/op Go ns/op delta
|
||||||
|
1kb 536 1500 +179.85%
|
||||||
|
10kb 26669 50777 +90.40%
|
||||||
|
50kb 252270 658534 +161.04%
|
||||||
|
100kb 686813 2127534 +209.77%
|
||||||
|
1Mb 12100000 22391830 +85.06%
|
||||||
|
5Mb 111731843 133550600 +19.53%
|
||||||
|
10Mb 212314000 318595800 +50.06%
|
||||||
|
20Mb 490196000 671512800 +36.99%
|
||||||
|
50Mb 1280000000 2451476000 +91.52%
|
||||||
|
100Mb 2673000000 5228991000 +95.62%
|
||||||
|
|
||||||
|
Benchmarks were run on a Core 2 Quad Q8200 (2.33GHz).
|
||||||
|
FFT is enabled when input numbers are over 200kbits.
|
||||||
|
|
||||||
|
Scanning large decimal number from strings.
|
||||||
|
(math/big [n^2 complexity] vs bigfft [n^1.6 complexity], Core i5-4590)
|
||||||
|
|
||||||
|
Digits old ns/op new ns/op delta
|
||||||
|
1e3 9995 10876 +8.81%
|
||||||
|
1e4 175356 243806 +39.03%
|
||||||
|
1e5 9427422 6780545 -28.08%
|
||||||
|
1e6 1776707489 144867502 -91.85%
|
||||||
|
2e6 6865499995 346540778 -94.95%
|
||||||
|
5e6 42641034189 1069878799 -97.49%
|
||||||
|
10e6 151975273589 2693328580 -98.23%
|
||||||
|
|
36
vendor/github.com/remyoudompheng/bigfft/arith_386.s
generated
vendored
Normal file
36
vendor/github.com/remyoudompheng/bigfft/arith_386.s
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addMulVVW(SB)
|
||||||
|
|
38
vendor/github.com/remyoudompheng/bigfft/arith_amd64.s
generated
vendored
Normal file
38
vendor/github.com/remyoudompheng/bigfft/arith_amd64.s
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
// (same as addVV except for SBBQ instead of ADCQ and label names)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addMulVVW(SB)
|
||||||
|
|
36
vendor/github.com/remyoudompheng/bigfft/arith_arm.s
generated
vendored
Normal file
36
vendor/github.com/remyoudompheng/bigfft/arith_arm.s
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
B math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
B math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
B math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
B math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·addMulVVW(SB)
|
||||||
|
|
36
vendor/github.com/remyoudompheng/bigfft/arith_arm64.s
generated
vendored
Normal file
36
vendor/github.com/remyoudompheng/bigfft/arith_arm64.s
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
B math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
B math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
B math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
B math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
B math∕big·addMulVVW(SB)
|
||||||
|
|
16
vendor/github.com/remyoudompheng/bigfft/arith_decl.go
generated
vendored
Normal file
16
vendor/github.com/remyoudompheng/bigfft/arith_decl.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package bigfft
|
||||||
|
|
||||||
|
import . "math/big"
|
||||||
|
|
||||||
|
// implemented in arith_$GOARCH.s
|
||||||
|
func addVV(z, x, y []Word) (c Word)
|
||||||
|
func subVV(z, x, y []Word) (c Word)
|
||||||
|
func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
func addMulVVW(z, x []Word, y Word) (c Word)
|
40
vendor/github.com/remyoudompheng/bigfft/arith_mips64x.s
generated
vendored
Normal file
40
vendor/github.com/remyoudompheng/bigfft/arith_mips64x.s
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
// +build mips64 mips64le
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
// (same as addVV except for SBBQ instead of ADCQ and label names)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addMulVVW(SB)
|
||||||
|
|
40
vendor/github.com/remyoudompheng/bigfft/arith_mipsx.s
generated
vendored
Normal file
40
vendor/github.com/remyoudompheng/bigfft/arith_mipsx.s
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
// +build mips mipsle
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
// (same as addVV except for SBBQ instead of ADCQ and label names)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
// (same as addVW except for SUBQ/SBBQ instead of ADDQ/ADCQ and label names)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
JMP math∕big·addMulVVW(SB)
|
||||||
|
|
38
vendor/github.com/remyoudompheng/bigfft/arith_ppc64x.s
generated
vendored
Normal file
38
vendor/github.com/remyoudompheng/bigfft/arith_ppc64x.s
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
// +build ppc64 ppc64le
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·addMulVVW(SB)
|
||||||
|
|
37
vendor/github.com/remyoudompheng/bigfft/arith_s390x.s
generated
vendored
Normal file
37
vendor/github.com/remyoudompheng/bigfft/arith_s390x.s
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
|
||||||
|
// Trampolines to math/big assembly implementations.
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func addVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·addVV(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·addVV(SB)
|
||||||
|
|
||||||
|
// func subVV(z, x, y []Word) (c Word)
|
||||||
|
TEXT ·subVV(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·subVV(SB)
|
||||||
|
|
||||||
|
// func addVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addVW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·addVW(SB)
|
||||||
|
|
||||||
|
// func subVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·subVW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·subVW(SB)
|
||||||
|
|
||||||
|
// func shlVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shlVU(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·shlVU(SB)
|
||||||
|
|
||||||
|
// func shrVU(z, x []Word, s uint) (c Word)
|
||||||
|
TEXT ·shrVU(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·shrVU(SB)
|
||||||
|
|
||||||
|
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
|
||||||
|
TEXT ·mulAddVWW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·mulAddVWW(SB)
|
||||||
|
|
||||||
|
// func addMulVVW(z, x []Word, y Word) (c Word)
|
||||||
|
TEXT ·addMulVVW(SB),NOSPLIT,$0
|
||||||
|
BR math∕big·addMulVVW(SB)
|
||||||
|
|
216
vendor/github.com/remyoudompheng/bigfft/fermat.go
generated
vendored
Normal file
216
vendor/github.com/remyoudompheng/bigfft/fermat.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
package bigfft
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Arithmetic modulo 2^n+1.
|
||||||
|
|
||||||
|
// A fermat of length w+1 represents a number modulo 2^(w*_W) + 1. The last
|
||||||
|
// word is zero or one. A number has at most two representatives satisfying the
|
||||||
|
// 0-1 last word constraint.
|
||||||
|
type fermat nat
|
||||||
|
|
||||||
|
func (n fermat) String() string { return nat(n).String() }
|
||||||
|
|
||||||
|
func (z fermat) norm() {
|
||||||
|
n := len(z) - 1
|
||||||
|
c := z[n]
|
||||||
|
if c == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if z[0] >= c {
|
||||||
|
z[n] = 0
|
||||||
|
z[0] -= c
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// z[0] < z[n].
|
||||||
|
subVW(z, z, c) // Substract c
|
||||||
|
if c > 1 {
|
||||||
|
z[n] -= c - 1
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
// Add back c.
|
||||||
|
if z[n] == 1 {
|
||||||
|
z[n] = 0
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
addVW(z, z, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift computes (x << k) mod (2^n+1).
|
||||||
|
func (z fermat) Shift(x fermat, k int) {
|
||||||
|
if len(z) != len(x) {
|
||||||
|
panic("len(z) != len(x) in Shift")
|
||||||
|
}
|
||||||
|
n := len(x) - 1
|
||||||
|
// Shift by n*_W is taking the opposite.
|
||||||
|
k %= 2 * n * _W
|
||||||
|
if k < 0 {
|
||||||
|
k += 2 * n * _W
|
||||||
|
}
|
||||||
|
neg := false
|
||||||
|
if k >= n*_W {
|
||||||
|
k -= n * _W
|
||||||
|
neg = true
|
||||||
|
}
|
||||||
|
|
||||||
|
kw, kb := k/_W, k%_W
|
||||||
|
|
||||||
|
z[n] = 1 // Add (-1)
|
||||||
|
if !neg {
|
||||||
|
for i := 0; i < kw; i++ {
|
||||||
|
z[i] = 0
|
||||||
|
}
|
||||||
|
// Shift left by kw words.
|
||||||
|
// x = a·2^(n-k) + b
|
||||||
|
// x<<k = (b<<k) - a
|
||||||
|
copy(z[kw:], x[:n-kw])
|
||||||
|
b := subVV(z[:kw+1], z[:kw+1], x[n-kw:])
|
||||||
|
if z[kw+1] > 0 {
|
||||||
|
z[kw+1] -= b
|
||||||
|
} else {
|
||||||
|
subVW(z[kw+1:], z[kw+1:], b)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i := kw + 1; i < n; i++ {
|
||||||
|
z[i] = 0
|
||||||
|
}
|
||||||
|
// Shift left and negate, by kw words.
|
||||||
|
copy(z[:kw+1], x[n-kw:n+1]) // z_low = x_high
|
||||||
|
b := subVV(z[kw:n], z[kw:n], x[:n-kw]) // z_high -= x_low
|
||||||
|
z[n] -= b
|
||||||
|
}
|
||||||
|
// Add back 1.
|
||||||
|
if z[n] > 0 {
|
||||||
|
z[n]--
|
||||||
|
} else if z[0] < ^big.Word(0) {
|
||||||
|
z[0]++
|
||||||
|
} else {
|
||||||
|
addVW(z, z, 1)
|
||||||
|
}
|
||||||
|
// Shift left by kb bits
|
||||||
|
shlVU(z, z, uint(kb))
|
||||||
|
z.norm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShiftHalf shifts x by k/2 bits the left. Shifting by 1/2 bit
|
||||||
|
// is multiplication by sqrt(2) mod 2^n+1 which is 2^(3n/4) - 2^(n/4).
|
||||||
|
// A temporary buffer must be provided in tmp.
|
||||||
|
func (z fermat) ShiftHalf(x fermat, k int, tmp fermat) {
|
||||||
|
n := len(z) - 1
|
||||||
|
if k%2 == 0 {
|
||||||
|
z.Shift(x, k/2)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := (k - 1) / 2
|
||||||
|
a := u + (3*_W/4)*n
|
||||||
|
b := u + (_W/4)*n
|
||||||
|
z.Shift(x, a)
|
||||||
|
tmp.Shift(x, b)
|
||||||
|
z.Sub(z, tmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add computes addition mod 2^n+1.
|
||||||
|
func (z fermat) Add(x, y fermat) fermat {
|
||||||
|
if len(z) != len(x) {
|
||||||
|
panic("Add: len(z) != len(x)")
|
||||||
|
}
|
||||||
|
addVV(z, x, y) // there cannot be a carry here.
|
||||||
|
z.norm()
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sub computes substraction mod 2^n+1.
|
||||||
|
func (z fermat) Sub(x, y fermat) fermat {
|
||||||
|
if len(z) != len(x) {
|
||||||
|
panic("Add: len(z) != len(x)")
|
||||||
|
}
|
||||||
|
n := len(y) - 1
|
||||||
|
b := subVV(z[:n], x[:n], y[:n])
|
||||||
|
b += y[n]
|
||||||
|
// If b > 0, we need to subtract b<<n, which is the same as adding b.
|
||||||
|
z[n] = x[n]
|
||||||
|
if z[0] <= ^big.Word(0)-b {
|
||||||
|
z[0] += b
|
||||||
|
} else {
|
||||||
|
addVW(z, z, b)
|
||||||
|
}
|
||||||
|
z.norm()
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z fermat) Mul(x, y fermat) fermat {
|
||||||
|
if len(x) != len(y) {
|
||||||
|
panic("Mul: len(x) != len(y)")
|
||||||
|
}
|
||||||
|
n := len(x) - 1
|
||||||
|
if n < 30 {
|
||||||
|
z = z[:2*n+2]
|
||||||
|
basicMul(z, x, y)
|
||||||
|
z = z[:2*n+1]
|
||||||
|
} else {
|
||||||
|
var xi, yi, zi big.Int
|
||||||
|
xi.SetBits(x)
|
||||||
|
yi.SetBits(y)
|
||||||
|
zi.SetBits(z)
|
||||||
|
zb := zi.Mul(&xi, &yi).Bits()
|
||||||
|
if len(zb) <= n {
|
||||||
|
// Short product.
|
||||||
|
copy(z, zb)
|
||||||
|
for i := len(zb); i < len(z); i++ {
|
||||||
|
z[i] = 0
|
||||||
|
}
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
z = zb
|
||||||
|
}
|
||||||
|
// len(z) is at most 2n+1.
|
||||||
|
if len(z) > 2*n+1 {
|
||||||
|
panic("len(z) > 2n+1")
|
||||||
|
}
|
||||||
|
// We now have
|
||||||
|
// z = z[:n] + 1<<(n*W) * z[n:2n+1]
|
||||||
|
// which normalizes to:
|
||||||
|
// z = z[:n] - z[n:2n] + z[2n]
|
||||||
|
c1 := big.Word(0)
|
||||||
|
if len(z) > 2*n {
|
||||||
|
c1 = addVW(z[:n], z[:n], z[2*n])
|
||||||
|
}
|
||||||
|
c2 := big.Word(0)
|
||||||
|
if len(z) >= 2*n {
|
||||||
|
c2 = subVV(z[:n], z[:n], z[n:2*n])
|
||||||
|
} else {
|
||||||
|
m := len(z) - n
|
||||||
|
c2 = subVV(z[:m], z[:m], z[n:])
|
||||||
|
c2 = subVW(z[m:n], z[m:n], c2)
|
||||||
|
}
|
||||||
|
// Restore carries.
|
||||||
|
// Substracting z[n] -= c2 is the same
|
||||||
|
// as z[0] += c2
|
||||||
|
z = z[:n+1]
|
||||||
|
z[n] = c1
|
||||||
|
c := addVW(z, z, c2)
|
||||||
|
if c != 0 {
|
||||||
|
panic("impossible")
|
||||||
|
}
|
||||||
|
z.norm()
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from math/big
|
||||||
|
//
|
||||||
|
// basicMul multiplies x and y and leaves the result in z.
|
||||||
|
// The (non-normalized) result is placed in z[0 : len(x) + len(y)].
|
||||||
|
func basicMul(z, x, y fermat) {
|
||||||
|
// initialize z
|
||||||
|
for i := 0; i < len(z); i++ {
|
||||||
|
z[i] = 0
|
||||||
|
}
|
||||||
|
for i, d := range y {
|
||||||
|
if d != 0 {
|
||||||
|
z[len(x)+i] = addMulVVW(z[i:i+len(x)], x, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
370
vendor/github.com/remyoudompheng/bigfft/fft.go
generated
vendored
Normal file
370
vendor/github.com/remyoudompheng/bigfft/fft.go
generated
vendored
Normal file
@ -0,0 +1,370 @@
|
|||||||
|
// Package bigfft implements multiplication of big.Int using FFT.
|
||||||
|
//
|
||||||
|
// The implementation is based on the Schönhage-Strassen method
|
||||||
|
// using integer FFT modulo 2^n+1.
|
||||||
|
package bigfft
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const _W = int(unsafe.Sizeof(big.Word(0)) * 8)
|
||||||
|
|
||||||
|
type nat []big.Word
|
||||||
|
|
||||||
|
func (n nat) String() string {
|
||||||
|
v := new(big.Int)
|
||||||
|
v.SetBits(n)
|
||||||
|
return v.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// fftThreshold is the size (in words) above which FFT is used over
|
||||||
|
// Karatsuba from math/big.
|
||||||
|
//
|
||||||
|
// TestCalibrate seems to indicate a threshold of 60kbits on 32-bit
|
||||||
|
// arches and 110kbits on 64-bit arches.
|
||||||
|
var fftThreshold = 1800
|
||||||
|
|
||||||
|
// Mul computes the product x*y and returns z.
|
||||||
|
// It can be used instead of the Mul method of
|
||||||
|
// *big.Int from math/big package.
|
||||||
|
func Mul(x, y *big.Int) *big.Int {
|
||||||
|
xwords := len(x.Bits())
|
||||||
|
ywords := len(y.Bits())
|
||||||
|
if xwords > fftThreshold && ywords > fftThreshold {
|
||||||
|
return mulFFT(x, y)
|
||||||
|
}
|
||||||
|
return new(big.Int).Mul(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mulFFT(x, y *big.Int) *big.Int {
|
||||||
|
var xb, yb nat = x.Bits(), y.Bits()
|
||||||
|
zb := fftmul(xb, yb)
|
||||||
|
z := new(big.Int)
|
||||||
|
z.SetBits(zb)
|
||||||
|
if x.Sign()*y.Sign() < 0 {
|
||||||
|
z.Neg(z)
|
||||||
|
}
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FFT size of K=1<<k is adequate when K is about 2*sqrt(N) where
|
||||||
|
// N = x.Bitlen() + y.Bitlen().
|
||||||
|
|
||||||
|
func fftmul(x, y nat) nat {
|
||||||
|
k, m := fftSize(x, y)
|
||||||
|
xp := polyFromNat(x, k, m)
|
||||||
|
yp := polyFromNat(y, k, m)
|
||||||
|
rp := xp.Mul(&yp)
|
||||||
|
return rp.Int()
|
||||||
|
}
|
||||||
|
|
||||||
|
// fftSizeThreshold[i] is the maximal size (in bits) where we should use
|
||||||
|
// fft size i.
|
||||||
|
var fftSizeThreshold = [...]int64{0, 0, 0,
|
||||||
|
4 << 10, 8 << 10, 16 << 10, // 5
|
||||||
|
32 << 10, 64 << 10, 1 << 18, 1 << 20, 3 << 20, // 10
|
||||||
|
8 << 20, 30 << 20, 100 << 20, 300 << 20, 600 << 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the FFT length k, m the number of words per chunk
|
||||||
|
// such that m << k is larger than the number of words
|
||||||
|
// in x*y.
|
||||||
|
func fftSize(x, y nat) (k uint, m int) {
|
||||||
|
words := len(x) + len(y)
|
||||||
|
bits := int64(words) * int64(_W)
|
||||||
|
k = uint(len(fftSizeThreshold))
|
||||||
|
for i := range fftSizeThreshold {
|
||||||
|
if fftSizeThreshold[i] > bits {
|
||||||
|
k = uint(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The 1<<k chunks of m words must have N bits so that
|
||||||
|
// 2^N-1 is larger than x*y. That is, m<<k > words
|
||||||
|
m = words>>k + 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// valueSize returns the length (in words) to use for polynomial
|
||||||
|
// coefficients, to compute a correct product of polynomials P*Q
|
||||||
|
// where deg(P*Q) < K (== 1<<k) and where coefficients of P and Q are
|
||||||
|
// less than b^m (== 1 << (m*_W)).
|
||||||
|
// The chosen length (in bits) must be a multiple of 1 << (k-extra).
|
||||||
|
func valueSize(k uint, m int, extra uint) int {
|
||||||
|
// The coefficients of P*Q are less than b^(2m)*K
|
||||||
|
// so we need W * valueSize >= 2*m*W+K
|
||||||
|
n := 2*m*_W + int(k) // necessary bits
|
||||||
|
K := 1 << (k - extra)
|
||||||
|
if K < _W {
|
||||||
|
K = _W
|
||||||
|
}
|
||||||
|
n = ((n / K) + 1) * K // round to a multiple of K
|
||||||
|
return n / _W
|
||||||
|
}
|
||||||
|
|
||||||
|
// poly represents an integer via a polynomial in Z[x]/(x^K+1)
|
||||||
|
// where K is the FFT length and b^m is the computation basis 1<<(m*_W).
|
||||||
|
// If P = a[0] + a[1] x + ... a[n] x^(K-1), the associated natural number
|
||||||
|
// is P(b^m).
|
||||||
|
type poly struct {
|
||||||
|
k uint // k is such that K = 1<<k.
|
||||||
|
m int // the m such that P(b^m) is the original number.
|
||||||
|
a []nat // a slice of at most K m-word coefficients.
|
||||||
|
}
|
||||||
|
|
||||||
|
// polyFromNat slices the number x into a polynomial
|
||||||
|
// with 1<<k coefficients made of m words.
|
||||||
|
func polyFromNat(x nat, k uint, m int) poly {
|
||||||
|
p := poly{k: k, m: m}
|
||||||
|
length := len(x)/m + 1
|
||||||
|
p.a = make([]nat, length)
|
||||||
|
for i := range p.a {
|
||||||
|
if len(x) < m {
|
||||||
|
p.a[i] = make(nat, m)
|
||||||
|
copy(p.a[i], x)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.a[i] = x[:m]
|
||||||
|
x = x[m:]
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int evaluates back a poly to its integer value.
|
||||||
|
func (p *poly) Int() nat {
|
||||||
|
length := len(p.a)*p.m + 1
|
||||||
|
if na := len(p.a); na > 0 {
|
||||||
|
length += len(p.a[na-1])
|
||||||
|
}
|
||||||
|
n := make(nat, length)
|
||||||
|
m := p.m
|
||||||
|
np := n
|
||||||
|
for i := range p.a {
|
||||||
|
l := len(p.a[i])
|
||||||
|
c := addVV(np[:l], np[:l], p.a[i])
|
||||||
|
if np[l] < ^big.Word(0) {
|
||||||
|
np[l] += c
|
||||||
|
} else {
|
||||||
|
addVW(np[l:], np[l:], c)
|
||||||
|
}
|
||||||
|
np = np[m:]
|
||||||
|
}
|
||||||
|
n = trim(n)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func trim(n nat) nat {
|
||||||
|
for i := range n {
|
||||||
|
if n[len(n)-1-i] != 0 {
|
||||||
|
return n[:len(n)-i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mul multiplies p and q modulo X^K-1, where K = 1<<p.k.
|
||||||
|
// The product is done via a Fourier transform.
|
||||||
|
func (p *poly) Mul(q *poly) poly {
|
||||||
|
// extra=2 because:
|
||||||
|
// * some power of 2 is a K-th root of unity when n is a multiple of K/2.
|
||||||
|
// * 2 itself is a square (see fermat.ShiftHalf)
|
||||||
|
n := valueSize(p.k, p.m, 2)
|
||||||
|
|
||||||
|
pv, qv := p.Transform(n), q.Transform(n)
|
||||||
|
rv := pv.Mul(&qv)
|
||||||
|
r := rv.InvTransform()
|
||||||
|
r.m = p.m
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// A polValues represents the value of a poly at the powers of a
|
||||||
|
// K-th root of unity θ=2^(l/2) in Z/(b^n+1)Z, where b^n = 2^(K/4*l).
|
||||||
|
type polValues struct {
|
||||||
|
k uint // k is such that K = 1<<k.
|
||||||
|
n int // the length of coefficients, n*_W a multiple of K/4.
|
||||||
|
values []fermat // a slice of K (n+1)-word values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform evaluates p at θ^i for i = 0...K-1, where
|
||||||
|
// θ is a K-th primitive root of unity in Z/(b^n+1)Z.
|
||||||
|
func (p *poly) Transform(n int) polValues {
|
||||||
|
k := p.k
|
||||||
|
inputbits := make([]big.Word, (n+1)<<k)
|
||||||
|
input := make([]fermat, 1<<k)
|
||||||
|
// Now computed q(ω^i) for i = 0 ... K-1
|
||||||
|
valbits := make([]big.Word, (n+1)<<k)
|
||||||
|
values := make([]fermat, 1<<k)
|
||||||
|
for i := range values {
|
||||||
|
input[i] = inputbits[i*(n+1) : (i+1)*(n+1)]
|
||||||
|
if i < len(p.a) {
|
||||||
|
copy(input[i], p.a[i])
|
||||||
|
}
|
||||||
|
values[i] = fermat(valbits[i*(n+1) : (i+1)*(n+1)])
|
||||||
|
}
|
||||||
|
fourier(values, input, false, n, k)
|
||||||
|
return polValues{k, n, values}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvTransform reconstructs p (modulo X^K - 1) from its
|
||||||
|
// values at θ^i for i = 0..K-1.
|
||||||
|
func (v *polValues) InvTransform() poly {
|
||||||
|
k, n := v.k, v.n
|
||||||
|
|
||||||
|
// Perform an inverse Fourier transform to recover p.
|
||||||
|
pbits := make([]big.Word, (n+1)<<k)
|
||||||
|
p := make([]fermat, 1<<k)
|
||||||
|
for i := range p {
|
||||||
|
p[i] = fermat(pbits[i*(n+1) : (i+1)*(n+1)])
|
||||||
|
}
|
||||||
|
fourier(p, v.values, true, n, k)
|
||||||
|
// Divide by K, and untwist q to recover p.
|
||||||
|
u := make(fermat, n+1)
|
||||||
|
a := make([]nat, 1<<k)
|
||||||
|
for i := range p {
|
||||||
|
u.Shift(p[i], -int(k))
|
||||||
|
copy(p[i], u)
|
||||||
|
a[i] = nat(p[i])
|
||||||
|
}
|
||||||
|
return poly{k: k, m: 0, a: a}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NTransform evaluates p at θω^i for i = 0...K-1, where
|
||||||
|
// θ is a (2K)-th primitive root of unity in Z/(b^n+1)Z
|
||||||
|
// and ω = θ².
|
||||||
|
func (p *poly) NTransform(n int) polValues {
|
||||||
|
k := p.k
|
||||||
|
if len(p.a) >= 1<<k {
|
||||||
|
panic("Transform: len(p.a) >= 1<<k")
|
||||||
|
}
|
||||||
|
// θ is represented as a shift.
|
||||||
|
θshift := (n * _W) >> k
|
||||||
|
// p(x) = a_0 + a_1 x + ... + a_{K-1} x^(K-1)
|
||||||
|
// p(θx) = q(x) where
|
||||||
|
// q(x) = a_0 + θa_1 x + ... + θ^(K-1) a_{K-1} x^(K-1)
|
||||||
|
//
|
||||||
|
// Twist p by θ to obtain q.
|
||||||
|
tbits := make([]big.Word, (n+1)<<k)
|
||||||
|
twisted := make([]fermat, 1<<k)
|
||||||
|
src := make(fermat, n+1)
|
||||||
|
for i := range twisted {
|
||||||
|
twisted[i] = fermat(tbits[i*(n+1) : (i+1)*(n+1)])
|
||||||
|
if i < len(p.a) {
|
||||||
|
for i := range src {
|
||||||
|
src[i] = 0
|
||||||
|
}
|
||||||
|
copy(src, p.a[i])
|
||||||
|
twisted[i].Shift(src, θshift*i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now computed q(ω^i) for i = 0 ... K-1
|
||||||
|
valbits := make([]big.Word, (n+1)<<k)
|
||||||
|
values := make([]fermat, 1<<k)
|
||||||
|
for i := range values {
|
||||||
|
values[i] = fermat(valbits[i*(n+1) : (i+1)*(n+1)])
|
||||||
|
}
|
||||||
|
fourier(values, twisted, false, n, k)
|
||||||
|
return polValues{k, n, values}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvTransform reconstructs a polynomial from its values at
|
||||||
|
// roots of x^K+1. The m field of the returned polynomial
|
||||||
|
// is unspecified.
|
||||||
|
func (v *polValues) InvNTransform() poly {
|
||||||
|
k := v.k
|
||||||
|
n := v.n
|
||||||
|
θshift := (n * _W) >> k
|
||||||
|
|
||||||
|
// Perform an inverse Fourier transform to recover q.
|
||||||
|
qbits := make([]big.Word, (n+1)<<k)
|
||||||
|
q := make([]fermat, 1<<k)
|
||||||
|
for i := range q {
|
||||||
|
q[i] = fermat(qbits[i*(n+1) : (i+1)*(n+1)])
|
||||||
|
}
|
||||||
|
fourier(q, v.values, true, n, k)
|
||||||
|
|
||||||
|
// Divide by K, and untwist q to recover p.
|
||||||
|
u := make(fermat, n+1)
|
||||||
|
a := make([]nat, 1<<k)
|
||||||
|
for i := range q {
|
||||||
|
u.Shift(q[i], -int(k)-i*θshift)
|
||||||
|
copy(q[i], u)
|
||||||
|
a[i] = nat(q[i])
|
||||||
|
}
|
||||||
|
return poly{k: k, m: 0, a: a}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fourier performs an unnormalized Fourier transform
|
||||||
|
// of src, a length 1<<k vector of numbers modulo b^n+1
|
||||||
|
// where b = 1<<_W.
|
||||||
|
func fourier(dst []fermat, src []fermat, backward bool, n int, k uint) {
|
||||||
|
var rec func(dst, src []fermat, size uint)
|
||||||
|
tmp := make(fermat, n+1) // pre-allocate temporary variables.
|
||||||
|
tmp2 := make(fermat, n+1) // pre-allocate temporary variables.
|
||||||
|
|
||||||
|
// The recursion function of the FFT.
|
||||||
|
// The root of unity used in the transform is ω=1<<(ω2shift/2).
|
||||||
|
// The source array may use shifted indices (i.e. the i-th
|
||||||
|
// element is src[i << idxShift]).
|
||||||
|
rec = func(dst, src []fermat, size uint) {
|
||||||
|
idxShift := k - size
|
||||||
|
ω2shift := (4 * n * _W) >> size
|
||||||
|
if backward {
|
||||||
|
ω2shift = -ω2shift
|
||||||
|
}
|
||||||
|
|
||||||
|
// Easy cases.
|
||||||
|
if len(src[0]) != n+1 || len(dst[0]) != n+1 {
|
||||||
|
panic("len(src[0]) != n+1 || len(dst[0]) != n+1")
|
||||||
|
}
|
||||||
|
switch size {
|
||||||
|
case 0:
|
||||||
|
copy(dst[0], src[0])
|
||||||
|
return
|
||||||
|
case 1:
|
||||||
|
dst[0].Add(src[0], src[1<<idxShift]) // dst[0] = src[0] + src[1]
|
||||||
|
dst[1].Sub(src[0], src[1<<idxShift]) // dst[1] = src[0] - src[1]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let P(x) = src[0] + src[1<<idxShift] * x + ... + src[K-1 << idxShift] * x^(K-1)
|
||||||
|
// The P(x) = Q1(x²) + x*Q2(x²)
|
||||||
|
// where Q1's coefficients are src with indices shifted by 1
|
||||||
|
// where Q2's coefficients are src[1<<idxShift:] with indices shifted by 1
|
||||||
|
|
||||||
|
// Split destination vectors in halves.
|
||||||
|
dst1 := dst[:1<<(size-1)]
|
||||||
|
dst2 := dst[1<<(size-1):]
|
||||||
|
// Transform Q1 and Q2 in the halves.
|
||||||
|
rec(dst1, src, size-1)
|
||||||
|
rec(dst2, src[1<<idxShift:], size-1)
|
||||||
|
|
||||||
|
// Reconstruct P's transform from transforms of Q1 and Q2.
|
||||||
|
// dst[i] is dst1[i] + ω^i * dst2[i]
|
||||||
|
// dst[i + 1<<(k-1)] is dst1[i] + ω^(i+K/2) * dst2[i]
|
||||||
|
//
|
||||||
|
for i := range dst1 {
|
||||||
|
tmp.ShiftHalf(dst2[i], i*ω2shift, tmp2) // ω^i * dst2[i]
|
||||||
|
dst2[i].Sub(dst1[i], tmp)
|
||||||
|
dst1[i].Add(dst1[i], tmp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rec(dst, src, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mul returns the pointwise product of p and q.
|
||||||
|
func (p *polValues) Mul(q *polValues) (r polValues) {
|
||||||
|
n := p.n
|
||||||
|
r.k, r.n = p.k, p.n
|
||||||
|
r.values = make([]fermat, len(p.values))
|
||||||
|
bits := make([]big.Word, len(p.values)*(n+1))
|
||||||
|
buf := make(fermat, 8*n)
|
||||||
|
for i := range r.values {
|
||||||
|
r.values[i] = bits[i*(n+1) : (i+1)*(n+1)]
|
||||||
|
z := buf.Mul(p.values[i], q.values[i])
|
||||||
|
copy(r.values[i], z)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
70
vendor/github.com/remyoudompheng/bigfft/scan.go
generated
vendored
Normal file
70
vendor/github.com/remyoudompheng/bigfft/scan.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package bigfft
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FromDecimalString converts the base 10 string
|
||||||
|
// representation of a natural (non-negative) number
|
||||||
|
// into a *big.Int.
|
||||||
|
// Its asymptotic complexity is less than quadratic.
|
||||||
|
func FromDecimalString(s string) *big.Int {
|
||||||
|
var sc scanner
|
||||||
|
z := new(big.Int)
|
||||||
|
sc.scan(z, s)
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
type scanner struct {
|
||||||
|
// powers[i] is 10^(2^i * quadraticScanThreshold).
|
||||||
|
powers []*big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) chunkSize(size int) (int, *big.Int) {
|
||||||
|
if size <= quadraticScanThreshold {
|
||||||
|
panic("size < quadraticScanThreshold")
|
||||||
|
}
|
||||||
|
pow := uint(0)
|
||||||
|
for n := size; n > quadraticScanThreshold; n /= 2 {
|
||||||
|
pow++
|
||||||
|
}
|
||||||
|
// threshold * 2^(pow-1) <= size < threshold * 2^pow
|
||||||
|
return quadraticScanThreshold << (pow - 1), s.power(pow - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) power(k uint) *big.Int {
|
||||||
|
for i := len(s.powers); i <= int(k); i++ {
|
||||||
|
z := new(big.Int)
|
||||||
|
if i == 0 {
|
||||||
|
if quadraticScanThreshold%14 != 0 {
|
||||||
|
panic("quadraticScanThreshold % 14 != 0")
|
||||||
|
}
|
||||||
|
z.Exp(big.NewInt(1e14), big.NewInt(quadraticScanThreshold/14), nil)
|
||||||
|
} else {
|
||||||
|
z.Mul(s.powers[i-1], s.powers[i-1])
|
||||||
|
}
|
||||||
|
s.powers = append(s.powers, z)
|
||||||
|
}
|
||||||
|
return s.powers[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *scanner) scan(z *big.Int, str string) {
|
||||||
|
if len(str) <= quadraticScanThreshold {
|
||||||
|
z.SetString(str, 10)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sz, pow := s.chunkSize(len(str))
|
||||||
|
// Scan the left half.
|
||||||
|
s.scan(z, str[:len(str)-sz])
|
||||||
|
// FIXME: reuse temporaries.
|
||||||
|
left := Mul(z, pow)
|
||||||
|
// Scan the right half
|
||||||
|
s.scan(z, str[len(str)-sz:])
|
||||||
|
z.Add(z, left)
|
||||||
|
}
|
||||||
|
|
||||||
|
// quadraticScanThreshold is the number of digits
|
||||||
|
// below which big.Int.SetString is more efficient
|
||||||
|
// than subquadratic algorithms.
|
||||||
|
// 1232 digits fit in 4096 bits.
|
||||||
|
const quadraticScanThreshold = 1232
|
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
4
vendor/github.com/skip2/go-qrcode/.gitignore
generated
vendored
@ -1,4 +0,0 @@
|
|||||||
*.sw*
|
|
||||||
*.png
|
|
||||||
*.directory
|
|
||||||
qrcode/qrcode
|
|
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
8
vendor/github.com/skip2/go-qrcode/.travis.yml
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
language: go
|
|
||||||
|
|
||||||
go:
|
|
||||||
- 1.7
|
|
||||||
|
|
||||||
script:
|
|
||||||
- go test -v ./...
|
|
||||||
|
|
86
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
86
vendor/github.com/skip2/go-qrcode/README.md
generated
vendored
@ -1,86 +0,0 @@
|
|||||||
# go-qrcode #
|
|
||||||
|
|
||||||
<img src='https://skip.org/img/nyancat-youtube-qr.png' align='right'>
|
|
||||||
|
|
||||||
Package qrcode implements a QR Code encoder. [![Build Status](https://travis-ci.org/skip2/go-qrcode.svg?branch=master)](https://travis-ci.org/skip2/go-qrcode)
|
|
||||||
|
|
||||||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be encoded, with URLs being a popular choice :)
|
|
||||||
|
|
||||||
Each QR Code contains error recovery information to aid reading damaged or obscured codes. There are four levels of error recovery: Low, medium, high and highest. QR Codes with a higher recovery level are more robust to damage, at the cost of being physically larger.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
go get -u github.com/skip2/go-qrcode/...
|
|
||||||
|
|
||||||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
import qrcode "github.com/skip2/go-qrcode"
|
|
||||||
|
|
||||||
- **Create a 256x256 PNG image:**
|
|
||||||
|
|
||||||
var png []byte
|
|
||||||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)
|
|
||||||
|
|
||||||
- **Create a 256x256 PNG image and write to a file:**
|
|
||||||
|
|
||||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")
|
|
||||||
|
|
||||||
- **Create a 256x256 PNG image with custom colors and write to file:**
|
|
||||||
|
|
||||||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png")
|
|
||||||
|
|
||||||
All examples use the qrcode.Medium error Recovery Level and create a fixed 256x256px size QR Code. The last function creates a white on black instead of black on white QR Code.
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
[![godoc](https://godoc.org/github.com/skip2/go-qrcode?status.png)](https://godoc.org/github.com/skip2/go-qrcode)
|
|
||||||
|
|
||||||
## Demoapp
|
|
||||||
|
|
||||||
[http://go-qrcode.appspot.com](http://go-qrcode.appspot.com)
|
|
||||||
|
|
||||||
## CLI
|
|
||||||
|
|
||||||
A command-line tool `qrcode` will be built into `$GOPATH/bin/`.
|
|
||||||
|
|
||||||
```
|
|
||||||
qrcode -- QR Code encoder in Go
|
|
||||||
https://github.com/skip2/go-qrcode
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
-d disable QR Code border
|
|
||||||
-i invert black and white
|
|
||||||
-o string
|
|
||||||
out PNG file prefix, empty for stdout
|
|
||||||
-s int
|
|
||||||
image size (pixel) (default 256)
|
|
||||||
-t print as text-art on stdout
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
1. Arguments except for flags are joined by " " and used to generate QR code.
|
|
||||||
Default output is STDOUT, pipe to imagemagick command "display" to display
|
|
||||||
on any X server.
|
|
||||||
|
|
||||||
qrcode hello word | display
|
|
||||||
|
|
||||||
2. Save to file if "display" not available:
|
|
||||||
|
|
||||||
qrcode "homepage: https://github.com/skip2/go-qrcode" > out.png
|
|
||||||
|
|
||||||
```
|
|
||||||
## Maximum capacity
|
|
||||||
The maximum capacity of a QR Code varies according to the content encoded and the error recovery level. The maximum capacity is 2,953 bytes, 4,296 alphanumeric characters, 7,089 numeric digits, or a combination of these.
|
|
||||||
|
|
||||||
## Borderless QR Codes
|
|
||||||
|
|
||||||
To aid QR Code reading software, QR codes have a built in whitespace border.
|
|
||||||
|
|
||||||
If you know what you're doing, and don't want a border, see https://gist.github.com/skip2/7e3d8a82f5317df9be437f8ec8ec0b7d for how to do it. It's still recommended you include a border manually.
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
- [http://en.wikipedia.org/wiki/QR_code](http://en.wikipedia.org/wiki/QR_code)
|
|
||||||
- [ISO/IEC 18004:2006](http://www.iso.org/iso/catalogue_detail.htm?csnumber=43655) - Main QR Code specification (approx CHF 198,00)<br>
|
|
||||||
- [https://github.com/qpliu/qrencode-go/](https://github.com/qpliu/qrencode-go/) - alternative Go QR encoding library based on [ZXing](https://github.com/zxing/zxing)
|
|
273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go
generated
vendored
273
vendor/github.com/skip2/go-qrcode/bitset/bitset.go
generated
vendored
@ -1,273 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
// Package bitset implements an append only bit array.
|
|
||||||
//
|
|
||||||
// To create a Bitset and append some bits:
|
|
||||||
// // Bitset Contents
|
|
||||||
// b := bitset.New() // {}
|
|
||||||
// b.AppendBools(true, true, false) // {1, 1, 0}
|
|
||||||
// b.AppendBools(true) // {1, 1, 0, 1}
|
|
||||||
// b.AppendValue(0x02, 4) // {1, 1, 0, 1, 0, 0, 1, 0}
|
|
||||||
//
|
|
||||||
// To read values:
|
|
||||||
//
|
|
||||||
// len := b.Len() // 8
|
|
||||||
// v := b.At(0) // 1
|
|
||||||
// v = b.At(1) // 1
|
|
||||||
// v = b.At(2) // 0
|
|
||||||
// v = b.At(8) // 0
|
|
||||||
package bitset
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
b0 = false
|
|
||||||
b1 = true
|
|
||||||
)
|
|
||||||
|
|
||||||
// Bitset stores an array of bits.
|
|
||||||
type Bitset struct {
|
|
||||||
// The number of bits stored.
|
|
||||||
numBits int
|
|
||||||
|
|
||||||
// Storage for individual bits.
|
|
||||||
bits []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// New returns an initialised Bitset with optional initial bits v.
|
|
||||||
func New(v ...bool) *Bitset {
|
|
||||||
b := &Bitset{numBits: 0, bits: make([]byte, 0)}
|
|
||||||
b.AppendBools(v...)
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone returns a copy.
|
|
||||||
func Clone(from *Bitset) *Bitset {
|
|
||||||
return &Bitset{numBits: from.numBits, bits: from.bits[:]}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Substr returns a substring, consisting of the bits from indexes start to end.
|
|
||||||
func (b *Bitset) Substr(start int, end int) *Bitset {
|
|
||||||
if start > end || end > b.numBits {
|
|
||||||
log.Panicf("Out of range start=%d end=%d numBits=%d", start, end, b.numBits)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := New()
|
|
||||||
result.ensureCapacity(end - start)
|
|
||||||
|
|
||||||
for i := start; i < end; i++ {
|
|
||||||
if b.At(i) {
|
|
||||||
result.bits[result.numBits/8] |= 0x80 >> uint(result.numBits%8)
|
|
||||||
}
|
|
||||||
result.numBits++
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFromBase2String constructs and returns a Bitset from a string. The string
|
|
||||||
// consists of '1', '0' or ' ' characters, e.g. "1010 0101". The '1' and '0'
|
|
||||||
// characters represent true/false bits respectively, and ' ' characters are
|
|
||||||
// ignored.
|
|
||||||
//
|
|
||||||
// The function panics if the input string contains other characters.
|
|
||||||
func NewFromBase2String(b2string string) *Bitset {
|
|
||||||
b := &Bitset{numBits: 0, bits: make([]byte, 0)}
|
|
||||||
|
|
||||||
for _, c := range b2string {
|
|
||||||
switch c {
|
|
||||||
case '1':
|
|
||||||
b.AppendBools(true)
|
|
||||||
case '0':
|
|
||||||
b.AppendBools(false)
|
|
||||||
case ' ':
|
|
||||||
default:
|
|
||||||
log.Panicf("Invalid char %c in NewFromBase2String", c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendBytes appends a list of whole bytes.
|
|
||||||
func (b *Bitset) AppendBytes(data []byte) {
|
|
||||||
for _, d := range data {
|
|
||||||
b.AppendByte(d, 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendByte appends the numBits least significant bits from value.
|
|
||||||
func (b *Bitset) AppendByte(value byte, numBits int) {
|
|
||||||
b.ensureCapacity(numBits)
|
|
||||||
|
|
||||||
if numBits > 8 {
|
|
||||||
log.Panicf("numBits %d out of range 0-8", numBits)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := numBits - 1; i >= 0; i-- {
|
|
||||||
if value&(1<<uint(i)) != 0 {
|
|
||||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.numBits++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendUint32 appends the numBits least significant bits from value.
|
|
||||||
func (b *Bitset) AppendUint32(value uint32, numBits int) {
|
|
||||||
b.ensureCapacity(numBits)
|
|
||||||
|
|
||||||
if numBits > 32 {
|
|
||||||
log.Panicf("numBits %d out of range 0-32", numBits)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := numBits - 1; i >= 0; i-- {
|
|
||||||
if value&(1<<uint(i)) != 0 {
|
|
||||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.numBits++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensureCapacity ensures the Bitset can store an additional |numBits|.
|
|
||||||
//
|
|
||||||
// The underlying array is expanded if necessary. To prevent frequent
|
|
||||||
// reallocation, expanding the underlying array at least doubles its capacity.
|
|
||||||
func (b *Bitset) ensureCapacity(numBits int) {
|
|
||||||
numBits += b.numBits
|
|
||||||
|
|
||||||
newNumBytes := numBits / 8
|
|
||||||
if numBits%8 != 0 {
|
|
||||||
newNumBytes++
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.bits) >= newNumBytes {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
b.bits = append(b.bits, make([]byte, newNumBytes+2*len(b.bits))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append bits copied from |other|.
|
|
||||||
//
|
|
||||||
// The new length is b.Len() + other.Len().
|
|
||||||
func (b *Bitset) Append(other *Bitset) {
|
|
||||||
b.ensureCapacity(other.numBits)
|
|
||||||
|
|
||||||
for i := 0; i < other.numBits; i++ {
|
|
||||||
if other.At(i) {
|
|
||||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
|
||||||
}
|
|
||||||
b.numBits++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendBools appends bits to the Bitset.
|
|
||||||
func (b *Bitset) AppendBools(bits ...bool) {
|
|
||||||
b.ensureCapacity(len(bits))
|
|
||||||
|
|
||||||
for _, v := range bits {
|
|
||||||
if v {
|
|
||||||
b.bits[b.numBits/8] |= 0x80 >> uint(b.numBits%8)
|
|
||||||
}
|
|
||||||
b.numBits++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendNumBools appends num bits of value value.
|
|
||||||
func (b *Bitset) AppendNumBools(num int, value bool) {
|
|
||||||
for i := 0; i < num; i++ {
|
|
||||||
b.AppendBools(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a human readable representation of the Bitset's contents.
|
|
||||||
func (b *Bitset) String() string {
|
|
||||||
var bitString string
|
|
||||||
for i := 0; i < b.numBits; i++ {
|
|
||||||
if (i % 8) == 0 {
|
|
||||||
bitString += " "
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b.bits[i/8] & (0x80 >> byte(i%8))) != 0 {
|
|
||||||
bitString += "1"
|
|
||||||
} else {
|
|
||||||
bitString += "0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("numBits=%d, bits=%s", b.numBits, bitString)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the length of the Bitset in bits.
|
|
||||||
func (b *Bitset) Len() int {
|
|
||||||
return b.numBits
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bits returns the contents of the Bitset.
|
|
||||||
func (b *Bitset) Bits() []bool {
|
|
||||||
result := make([]bool, b.numBits)
|
|
||||||
|
|
||||||
var i int
|
|
||||||
for i = 0; i < b.numBits; i++ {
|
|
||||||
result[i] = (b.bits[i/8] & (0x80 >> byte(i%8))) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// At returns the value of the bit at |index|.
|
|
||||||
func (b *Bitset) At(index int) bool {
|
|
||||||
if index >= b.numBits {
|
|
||||||
log.Panicf("Index %d out of range", index)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (b.bits[index/8] & (0x80 >> byte(index%8))) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals returns true if the Bitset equals other.
|
|
||||||
func (b *Bitset) Equals(other *Bitset) bool {
|
|
||||||
if b.numBits != other.numBits {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(b.bits[0:b.numBits/8], other.bits[0:b.numBits/8]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 8 * (b.numBits / 8); i < b.numBits; i++ {
|
|
||||||
a := (b.bits[i/8] & (0x80 >> byte(i%8)))
|
|
||||||
b := (other.bits[i/8] & (0x80 >> byte(i%8)))
|
|
||||||
|
|
||||||
if a != b {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByteAt returns a byte consisting of upto 8 bits starting at index.
|
|
||||||
func (b *Bitset) ByteAt(index int) byte {
|
|
||||||
if index < 0 || index >= b.numBits {
|
|
||||||
log.Panicf("Index %d out of range", index)
|
|
||||||
}
|
|
||||||
|
|
||||||
var result byte
|
|
||||||
|
|
||||||
for i := index; i < index+8 && i < b.numBits; i++ {
|
|
||||||
result <<= 1
|
|
||||||
if b.At(i) {
|
|
||||||
result |= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
486
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
486
vendor/github.com/skip2/go-qrcode/encoder.go
generated
vendored
@ -1,486 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
package qrcode
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
bitset "github.com/skip2/go-qrcode/bitset"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Data encoding.
|
|
||||||
//
|
|
||||||
// The main data portion of a QR Code consists of one or more segments of data.
|
|
||||||
// A segment consists of:
|
|
||||||
//
|
|
||||||
// - The segment Data Mode: numeric, alphanumeric, or byte.
|
|
||||||
// - The length of segment in bits.
|
|
||||||
// - Encoded data.
|
|
||||||
//
|
|
||||||
// For example, the string "123ZZ#!#!" may be represented as:
|
|
||||||
//
|
|
||||||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"]
|
|
||||||
//
|
|
||||||
// Multiple data modes exist to minimise the size of encoded data. For example,
|
|
||||||
// 8-bit bytes require 8 bits to encode each, but base 10 numeric data can be
|
|
||||||
// encoded at a higher density of 3 numbers (e.g. 123) per 10 bits.
|
|
||||||
//
|
|
||||||
// Some data can be represented in multiple modes. Numeric data can be
|
|
||||||
// represented in all three modes, whereas alphanumeric data (e.g. 'A') can be
|
|
||||||
// represented in alphanumeric and byte mode.
|
|
||||||
//
|
|
||||||
// Starting a new segment (to use a different Data Mode) has a cost, the bits to
|
|
||||||
// state the new segment Data Mode and length. To minimise each QR Code's symbol
|
|
||||||
// size, an optimisation routine coalesces segment types where possible, to
|
|
||||||
// reduce the encoded data length.
|
|
||||||
//
|
|
||||||
// There are several other data modes available (e.g. Kanji mode) which are not
|
|
||||||
// implemented here.
|
|
||||||
|
|
||||||
// A segment encoding mode.
|
|
||||||
type dataMode uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Each dataMode is a subset of the subsequent dataMode:
|
|
||||||
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
|
||||||
//
|
|
||||||
// This ordering is important for determining which data modes a character can
|
|
||||||
// be encoded with. E.g. 'E' can be encoded in both dataModeAlphanumeric and
|
|
||||||
// dataModeByte.
|
|
||||||
dataModeNone dataMode = 1 << iota
|
|
||||||
dataModeNumeric
|
|
||||||
dataModeAlphanumeric
|
|
||||||
dataModeByte
|
|
||||||
)
|
|
||||||
|
|
||||||
// dataModeString returns d as a short printable string.
|
|
||||||
func dataModeString(d dataMode) string {
|
|
||||||
switch d {
|
|
||||||
case dataModeNone:
|
|
||||||
return "none"
|
|
||||||
case dataModeNumeric:
|
|
||||||
return "numeric"
|
|
||||||
case dataModeAlphanumeric:
|
|
||||||
return "alphanumeric"
|
|
||||||
case dataModeByte:
|
|
||||||
return "byte"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
type dataEncoderType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
dataEncoderType1To9 dataEncoderType = iota
|
|
||||||
dataEncoderType10To26
|
|
||||||
dataEncoderType27To40
|
|
||||||
)
|
|
||||||
|
|
||||||
// segment is a single segment of data.
|
|
||||||
type segment struct {
|
|
||||||
// Data Mode (e.g. numeric).
|
|
||||||
dataMode dataMode
|
|
||||||
|
|
||||||
// segment data (e.g. "abc").
|
|
||||||
data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// A dataEncoder encodes data for a particular QR Code version.
|
|
||||||
type dataEncoder struct {
|
|
||||||
// Minimum & maximum versions supported.
|
|
||||||
minVersion int
|
|
||||||
maxVersion int
|
|
||||||
|
|
||||||
// Mode indicator bit sequences.
|
|
||||||
numericModeIndicator *bitset.Bitset
|
|
||||||
alphanumericModeIndicator *bitset.Bitset
|
|
||||||
byteModeIndicator *bitset.Bitset
|
|
||||||
|
|
||||||
// Character count lengths.
|
|
||||||
numNumericCharCountBits int
|
|
||||||
numAlphanumericCharCountBits int
|
|
||||||
numByteCharCountBits int
|
|
||||||
|
|
||||||
// The raw input data.
|
|
||||||
data []byte
|
|
||||||
|
|
||||||
// The data classified into unoptimised segments.
|
|
||||||
actual []segment
|
|
||||||
|
|
||||||
// The data classified into optimised segments.
|
|
||||||
optimised []segment
|
|
||||||
}
|
|
||||||
|
|
||||||
// newDataEncoder constructs a dataEncoder.
|
|
||||||
func newDataEncoder(t dataEncoderType) *dataEncoder {
|
|
||||||
d := &dataEncoder{}
|
|
||||||
|
|
||||||
switch t {
|
|
||||||
case dataEncoderType1To9:
|
|
||||||
d = &dataEncoder{
|
|
||||||
minVersion: 1,
|
|
||||||
maxVersion: 9,
|
|
||||||
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
|
||||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
|
||||||
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
|
||||||
numNumericCharCountBits: 10,
|
|
||||||
numAlphanumericCharCountBits: 9,
|
|
||||||
numByteCharCountBits: 8,
|
|
||||||
}
|
|
||||||
case dataEncoderType10To26:
|
|
||||||
d = &dataEncoder{
|
|
||||||
minVersion: 10,
|
|
||||||
maxVersion: 26,
|
|
||||||
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
|
||||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
|
||||||
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
|
||||||
numNumericCharCountBits: 12,
|
|
||||||
numAlphanumericCharCountBits: 11,
|
|
||||||
numByteCharCountBits: 16,
|
|
||||||
}
|
|
||||||
case dataEncoderType27To40:
|
|
||||||
d = &dataEncoder{
|
|
||||||
minVersion: 27,
|
|
||||||
maxVersion: 40,
|
|
||||||
numericModeIndicator: bitset.New(b0, b0, b0, b1),
|
|
||||||
alphanumericModeIndicator: bitset.New(b0, b0, b1, b0),
|
|
||||||
byteModeIndicator: bitset.New(b0, b1, b0, b0),
|
|
||||||
numNumericCharCountBits: 14,
|
|
||||||
numAlphanumericCharCountBits: 13,
|
|
||||||
numByteCharCountBits: 16,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Panic("Unknown dataEncoderType")
|
|
||||||
}
|
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode data as one or more segments and return the encoded data.
|
|
||||||
//
|
|
||||||
// The returned data does not include the terminator bit sequence.
|
|
||||||
func (d *dataEncoder) encode(data []byte) (*bitset.Bitset, error) {
|
|
||||||
d.data = data
|
|
||||||
d.actual = nil
|
|
||||||
d.optimised = nil
|
|
||||||
|
|
||||||
if len(data) == 0 {
|
|
||||||
return nil, errors.New("no data to encode")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Classify data into unoptimised segments.
|
|
||||||
highestRequiredMode := d.classifyDataModes()
|
|
||||||
|
|
||||||
// Optimise segments.
|
|
||||||
err := d.optimiseDataModes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a single byte encoded segment would be more efficient.
|
|
||||||
optimizedLength := 0
|
|
||||||
for _, s := range d.optimised {
|
|
||||||
length, err := d.encodedLength(s.dataMode, len(s.data))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
optimizedLength += length
|
|
||||||
}
|
|
||||||
|
|
||||||
singleByteSegmentLength, err := d.encodedLength(highestRequiredMode, len(d.data))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if singleByteSegmentLength <= optimizedLength {
|
|
||||||
d.optimised = []segment{segment{dataMode: highestRequiredMode, data: d.data}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode data.
|
|
||||||
encoded := bitset.New()
|
|
||||||
for _, s := range d.optimised {
|
|
||||||
d.encodeDataRaw(s.data, s.dataMode, encoded)
|
|
||||||
}
|
|
||||||
|
|
||||||
return encoded, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// classifyDataModes classifies the raw data into unoptimised segments.
|
|
||||||
// e.g. "123ZZ#!#!" =>
|
|
||||||
// [numeric, 3, "123"] [alphanumeric, 2, "ZZ"] [byte, 4, "#!#!"].
|
|
||||||
//
|
|
||||||
// Returns the highest data mode needed to encode the data. e.g. for a mixed
|
|
||||||
// numeric/alphanumeric input, the highest is alphanumeric.
|
|
||||||
//
|
|
||||||
// dataModeNone < dataModeNumeric < dataModeAlphanumeric < dataModeByte
|
|
||||||
func (d *dataEncoder) classifyDataModes() dataMode {
|
|
||||||
var start int
|
|
||||||
mode := dataModeNone
|
|
||||||
highestRequiredMode := mode
|
|
||||||
|
|
||||||
for i, v := range d.data {
|
|
||||||
newMode := dataModeNone
|
|
||||||
switch {
|
|
||||||
case v >= 0x30 && v <= 0x39:
|
|
||||||
newMode = dataModeNumeric
|
|
||||||
case v == 0x20 || v == 0x24 || v == 0x25 || v == 0x2a || v == 0x2b || v ==
|
|
||||||
0x2d || v == 0x2e || v == 0x2f || v == 0x3a || (v >= 0x41 && v <= 0x5a):
|
|
||||||
newMode = dataModeAlphanumeric
|
|
||||||
default:
|
|
||||||
newMode = dataModeByte
|
|
||||||
}
|
|
||||||
|
|
||||||
if newMode != mode {
|
|
||||||
if i > 0 {
|
|
||||||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:i]})
|
|
||||||
|
|
||||||
start = i
|
|
||||||
}
|
|
||||||
|
|
||||||
mode = newMode
|
|
||||||
}
|
|
||||||
|
|
||||||
if newMode > highestRequiredMode {
|
|
||||||
highestRequiredMode = newMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
d.actual = append(d.actual, segment{dataMode: mode, data: d.data[start:len(d.data)]})
|
|
||||||
|
|
||||||
return highestRequiredMode
|
|
||||||
}
|
|
||||||
|
|
||||||
// optimiseDataModes optimises the list of segments to reduce the overall output
|
|
||||||
// encoded data length.
|
|
||||||
//
|
|
||||||
// The algorithm coalesces adjacent segments. segments are only coalesced when
|
|
||||||
// the Data Modes are compatible, and when the coalesced segment has a shorter
|
|
||||||
// encoded length than separate segments.
|
|
||||||
//
|
|
||||||
// Multiple segments may be coalesced. For example a string of alternating
|
|
||||||
// alphanumeric/numeric segments ANANANANA can be optimised to just A.
|
|
||||||
func (d *dataEncoder) optimiseDataModes() error {
|
|
||||||
for i := 0; i < len(d.actual); {
|
|
||||||
mode := d.actual[i].dataMode
|
|
||||||
numChars := len(d.actual[i].data)
|
|
||||||
|
|
||||||
j := i + 1
|
|
||||||
for j < len(d.actual) {
|
|
||||||
nextNumChars := len(d.actual[j].data)
|
|
||||||
nextMode := d.actual[j].dataMode
|
|
||||||
|
|
||||||
if nextMode > mode {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
coalescedLength, err := d.encodedLength(mode, numChars+nextNumChars)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
seperateLength1, err := d.encodedLength(mode, numChars)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
seperateLength2, err := d.encodedLength(nextMode, nextNumChars)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if coalescedLength < seperateLength1+seperateLength2 {
|
|
||||||
j++
|
|
||||||
numChars += nextNumChars
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
optimised := segment{dataMode: mode,
|
|
||||||
data: make([]byte, 0, numChars)}
|
|
||||||
|
|
||||||
for k := i; k < j; k++ {
|
|
||||||
optimised.data = append(optimised.data, d.actual[k].data...)
|
|
||||||
}
|
|
||||||
|
|
||||||
d.optimised = append(d.optimised, optimised)
|
|
||||||
|
|
||||||
i = j
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodeDataRaw encodes data in dataMode. The encoded data is appended to
|
|
||||||
// encoded.
|
|
||||||
func (d *dataEncoder) encodeDataRaw(data []byte, dataMode dataMode, encoded *bitset.Bitset) {
|
|
||||||
modeIndicator := d.modeIndicator(dataMode)
|
|
||||||
charCountBits := d.charCountBits(dataMode)
|
|
||||||
|
|
||||||
// Append mode indicator.
|
|
||||||
encoded.Append(modeIndicator)
|
|
||||||
|
|
||||||
// Append character count.
|
|
||||||
encoded.AppendUint32(uint32(len(data)), charCountBits)
|
|
||||||
|
|
||||||
// Append data.
|
|
||||||
switch dataMode {
|
|
||||||
case dataModeNumeric:
|
|
||||||
for i := 0; i < len(data); i += 3 {
|
|
||||||
charsRemaining := len(data) - i
|
|
||||||
|
|
||||||
var value uint32
|
|
||||||
bitsUsed := 1
|
|
||||||
|
|
||||||
for j := 0; j < charsRemaining && j < 3; j++ {
|
|
||||||
value *= 10
|
|
||||||
value += uint32(data[i+j] - 0x30)
|
|
||||||
bitsUsed += 3
|
|
||||||
}
|
|
||||||
encoded.AppendUint32(value, bitsUsed)
|
|
||||||
}
|
|
||||||
case dataModeAlphanumeric:
|
|
||||||
for i := 0; i < len(data); i += 2 {
|
|
||||||
charsRemaining := len(data) - i
|
|
||||||
|
|
||||||
var value uint32
|
|
||||||
for j := 0; j < charsRemaining && j < 2; j++ {
|
|
||||||
value *= 45
|
|
||||||
value += encodeAlphanumericCharacter(data[i+j])
|
|
||||||
}
|
|
||||||
|
|
||||||
bitsUsed := 6
|
|
||||||
if charsRemaining > 1 {
|
|
||||||
bitsUsed = 11
|
|
||||||
}
|
|
||||||
|
|
||||||
encoded.AppendUint32(value, bitsUsed)
|
|
||||||
}
|
|
||||||
case dataModeByte:
|
|
||||||
for _, b := range data {
|
|
||||||
encoded.AppendByte(b, 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// modeIndicator returns the segment header bits for a segment of type dataMode.
|
|
||||||
func (d *dataEncoder) modeIndicator(dataMode dataMode) *bitset.Bitset {
|
|
||||||
switch dataMode {
|
|
||||||
case dataModeNumeric:
|
|
||||||
return d.numericModeIndicator
|
|
||||||
case dataModeAlphanumeric:
|
|
||||||
return d.alphanumericModeIndicator
|
|
||||||
case dataModeByte:
|
|
||||||
return d.byteModeIndicator
|
|
||||||
default:
|
|
||||||
log.Panic("Unknown data mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// charCountBits returns the number of bits used to encode the length of a data
|
|
||||||
// segment of type dataMode.
|
|
||||||
func (d *dataEncoder) charCountBits(dataMode dataMode) int {
|
|
||||||
switch dataMode {
|
|
||||||
case dataModeNumeric:
|
|
||||||
return d.numNumericCharCountBits
|
|
||||||
case dataModeAlphanumeric:
|
|
||||||
return d.numAlphanumericCharCountBits
|
|
||||||
case dataModeByte:
|
|
||||||
return d.numByteCharCountBits
|
|
||||||
default:
|
|
||||||
log.Panic("Unknown data mode")
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodedLength returns the number of bits required to encode n symbols in
|
|
||||||
// dataMode.
|
|
||||||
//
|
|
||||||
// The number of bits required is affected by:
|
|
||||||
// - QR code type - Mode Indicator length.
|
|
||||||
// - Data mode - number of bits used to represent data length.
|
|
||||||
// - Data mode - how the data is encoded.
|
|
||||||
// - Number of symbols encoded.
|
|
||||||
//
|
|
||||||
// An error is returned if the mode is not supported, or the length requested is
|
|
||||||
// too long to be represented.
|
|
||||||
func (d *dataEncoder) encodedLength(dataMode dataMode, n int) (int, error) {
|
|
||||||
modeIndicator := d.modeIndicator(dataMode)
|
|
||||||
charCountBits := d.charCountBits(dataMode)
|
|
||||||
|
|
||||||
if modeIndicator == nil {
|
|
||||||
return 0, errors.New("mode not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
maxLength := (1 << uint8(charCountBits)) - 1
|
|
||||||
|
|
||||||
if n > maxLength {
|
|
||||||
return 0, errors.New("length too long to be represented")
|
|
||||||
}
|
|
||||||
|
|
||||||
length := modeIndicator.Len() + charCountBits
|
|
||||||
|
|
||||||
switch dataMode {
|
|
||||||
case dataModeNumeric:
|
|
||||||
length += 10 * (n / 3)
|
|
||||||
|
|
||||||
if n%3 != 0 {
|
|
||||||
length += 1 + 3*(n%3)
|
|
||||||
}
|
|
||||||
case dataModeAlphanumeric:
|
|
||||||
length += 11 * (n / 2)
|
|
||||||
length += 6 * (n % 2)
|
|
||||||
case dataModeByte:
|
|
||||||
length += 8 * n
|
|
||||||
}
|
|
||||||
|
|
||||||
return length, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodeAlphanumericChar returns the QR Code encoded value of v.
|
|
||||||
//
|
|
||||||
// v must be a QR Code defined alphanumeric character: 0-9, A-Z, SP, $%*+-./ or
|
|
||||||
// :. The characters are mapped to values in the range 0-44 respectively.
|
|
||||||
func encodeAlphanumericCharacter(v byte) uint32 {
|
|
||||||
c := uint32(v)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case c >= '0' && c <= '9':
|
|
||||||
// 0-9 encoded as 0-9.
|
|
||||||
return c - '0'
|
|
||||||
case c >= 'A' && c <= 'Z':
|
|
||||||
// A-Z encoded as 10-35.
|
|
||||||
return c - 'A' + 10
|
|
||||||
case c == ' ':
|
|
||||||
return 36
|
|
||||||
case c == '$':
|
|
||||||
return 37
|
|
||||||
case c == '%':
|
|
||||||
return 38
|
|
||||||
case c == '*':
|
|
||||||
return 39
|
|
||||||
case c == '+':
|
|
||||||
return 40
|
|
||||||
case c == '-':
|
|
||||||
return 41
|
|
||||||
case c == '.':
|
|
||||||
return 42
|
|
||||||
case c == '/':
|
|
||||||
return 43
|
|
||||||
case c == ':':
|
|
||||||
return 44
|
|
||||||
default:
|
|
||||||
log.Panicf("encodeAlphanumericCharacter() with non alphanumeric char %v.", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
608
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
608
vendor/github.com/skip2/go-qrcode/qrcode.go
generated
vendored
@ -1,608 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
/*
|
|
||||||
Package qrcode implements a QR Code encoder.
|
|
||||||
|
|
||||||
A QR Code is a matrix (two-dimensional) barcode. Arbitrary content may be
|
|
||||||
encoded.
|
|
||||||
|
|
||||||
A QR Code contains error recovery information to aid reading damaged or
|
|
||||||
obscured codes. There are four levels of error recovery: qrcode.{Low, Medium,
|
|
||||||
High, Highest}. QR Codes with a higher recovery level are more robust to damage,
|
|
||||||
at the cost of being physically larger.
|
|
||||||
|
|
||||||
Three functions cover most use cases:
|
|
||||||
|
|
||||||
- Create a PNG image:
|
|
||||||
|
|
||||||
var png []byte
|
|
||||||
png, err := qrcode.Encode("https://example.org", qrcode.Medium, 256)
|
|
||||||
|
|
||||||
- Create a PNG image and write to a file:
|
|
||||||
|
|
||||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, 256, "qr.png")
|
|
||||||
|
|
||||||
- Create a PNG image with custom colors and write to file:
|
|
||||||
|
|
||||||
err := qrcode.WriteColorFile("https://example.org", qrcode.Medium, 256, color.Black, color.White, "qr.png")
|
|
||||||
|
|
||||||
All examples use the qrcode.Medium error Recovery Level and create a fixed
|
|
||||||
256x256px size QR Code. The last function creates a white on black instead of black
|
|
||||||
on white QR Code.
|
|
||||||
|
|
||||||
To generate a variable sized image instead, specify a negative size (in place of
|
|
||||||
the 256 above), such as -4 or -5. Larger negative numbers create larger images:
|
|
||||||
A size of -5 sets each module (QR Code "pixel") to be 5px wide/high.
|
|
||||||
|
|
||||||
- Create a PNG image (variable size, with minimum white padding) and write to a file:
|
|
||||||
|
|
||||||
err := qrcode.WriteFile("https://example.org", qrcode.Medium, -5, "qr.png")
|
|
||||||
|
|
||||||
The maximum capacity of a QR Code varies according to the content encoded and
|
|
||||||
the error recovery level. The maximum capacity is 2,953 bytes, 4,296
|
|
||||||
alphanumeric characters, 7,089 numeric digits, or a combination of these.
|
|
||||||
|
|
||||||
This package implements a subset of QR Code 2005, as defined in ISO/IEC
|
|
||||||
18004:2006.
|
|
||||||
*/
|
|
||||||
package qrcode
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"image/png"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
bitset "github.com/skip2/go-qrcode/bitset"
|
|
||||||
reedsolomon "github.com/skip2/go-qrcode/reedsolomon"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encode a QR Code and return a raw PNG image.
|
|
||||||
//
|
|
||||||
// size is both the image width and height in pixels. If size is too small then
|
|
||||||
// a larger image is silently returned. Negative values for size cause a
|
|
||||||
// variable sized image to be returned: See the documentation for Image().
|
|
||||||
//
|
|
||||||
// To serve over HTTP, remember to send a Content-Type: image/png header.
|
|
||||||
func Encode(content string, level RecoveryLevel, size int) ([]byte, error) {
|
|
||||||
var q *QRCode
|
|
||||||
|
|
||||||
q, err := New(content, level)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return q.PNG(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFile encodes, then writes a QR Code to the given filename in PNG format.
|
|
||||||
//
|
|
||||||
// size is both the image width and height in pixels. If size is too small then
|
|
||||||
// a larger image is silently written. Negative values for size cause a variable
|
|
||||||
// sized image to be written: See the documentation for Image().
|
|
||||||
func WriteFile(content string, level RecoveryLevel, size int, filename string) error {
|
|
||||||
var q *QRCode
|
|
||||||
|
|
||||||
q, err := New(content, level)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return q.WriteFile(size, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteColorFile encodes, then writes a QR Code to the given filename in PNG format.
|
|
||||||
// With WriteColorFile you can also specify the colors you want to use.
|
|
||||||
//
|
|
||||||
// size is both the image width and height in pixels. If size is too small then
|
|
||||||
// a larger image is silently written. Negative values for size cause a variable
|
|
||||||
// sized image to be written: See the documentation for Image().
|
|
||||||
func WriteColorFile(content string, level RecoveryLevel, size int, background,
|
|
||||||
foreground color.Color, filename string) error {
|
|
||||||
|
|
||||||
var q *QRCode
|
|
||||||
|
|
||||||
q, err := New(content, level)
|
|
||||||
|
|
||||||
q.BackgroundColor = background
|
|
||||||
q.ForegroundColor = foreground
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return q.WriteFile(size, filename)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A QRCode represents a valid encoded QRCode.
|
|
||||||
type QRCode struct {
|
|
||||||
// Original content encoded.
|
|
||||||
Content string
|
|
||||||
|
|
||||||
// QR Code type.
|
|
||||||
Level RecoveryLevel
|
|
||||||
VersionNumber int
|
|
||||||
|
|
||||||
// User settable drawing options.
|
|
||||||
ForegroundColor color.Color
|
|
||||||
BackgroundColor color.Color
|
|
||||||
|
|
||||||
// Disable the QR Code border.
|
|
||||||
DisableBorder bool
|
|
||||||
|
|
||||||
encoder *dataEncoder
|
|
||||||
version qrCodeVersion
|
|
||||||
|
|
||||||
data *bitset.Bitset
|
|
||||||
symbol *symbol
|
|
||||||
mask int
|
|
||||||
}
|
|
||||||
|
|
||||||
// New constructs a QRCode.
|
|
||||||
//
|
|
||||||
// var q *qrcode.QRCode
|
|
||||||
// q, err := qrcode.New("my content", qrcode.Medium)
|
|
||||||
//
|
|
||||||
// An error occurs if the content is too long.
|
|
||||||
func New(content string, level RecoveryLevel) (*QRCode, error) {
|
|
||||||
encoders := []dataEncoderType{dataEncoderType1To9, dataEncoderType10To26,
|
|
||||||
dataEncoderType27To40}
|
|
||||||
|
|
||||||
var encoder *dataEncoder
|
|
||||||
var encoded *bitset.Bitset
|
|
||||||
var chosenVersion *qrCodeVersion
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for _, t := range encoders {
|
|
||||||
encoder = newDataEncoder(t)
|
|
||||||
encoded, err = encoder.encode([]byte(content))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
chosenVersion = chooseQRCodeVersion(level, encoder, encoded.Len())
|
|
||||||
|
|
||||||
if chosenVersion != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if chosenVersion == nil {
|
|
||||||
return nil, errors.New("content too long to encode")
|
|
||||||
}
|
|
||||||
|
|
||||||
q := &QRCode{
|
|
||||||
Content: content,
|
|
||||||
|
|
||||||
Level: level,
|
|
||||||
VersionNumber: chosenVersion.version,
|
|
||||||
|
|
||||||
ForegroundColor: color.Black,
|
|
||||||
BackgroundColor: color.White,
|
|
||||||
|
|
||||||
encoder: encoder,
|
|
||||||
data: encoded,
|
|
||||||
version: *chosenVersion,
|
|
||||||
}
|
|
||||||
|
|
||||||
return q, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewWithForcedVersion constructs a QRCode of a specific version.
|
|
||||||
//
|
|
||||||
// var q *qrcode.QRCode
|
|
||||||
// q, err := qrcode.NewWithForcedVersion("my content", 25, qrcode.Medium)
|
|
||||||
//
|
|
||||||
// An error occurs in case of invalid version.
|
|
||||||
func NewWithForcedVersion(content string, version int, level RecoveryLevel) (*QRCode, error) {
|
|
||||||
var encoder *dataEncoder
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case version >= 1 && version <= 9:
|
|
||||||
encoder = newDataEncoder(dataEncoderType1To9)
|
|
||||||
case version >= 10 && version <= 26:
|
|
||||||
encoder = newDataEncoder(dataEncoderType10To26)
|
|
||||||
case version >= 27 && version <= 40:
|
|
||||||
encoder = newDataEncoder(dataEncoderType27To40)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("Invalid version %d (expected 1-40 inclusive)", version)
|
|
||||||
}
|
|
||||||
|
|
||||||
var encoded *bitset.Bitset
|
|
||||||
encoded, err := encoder.encode([]byte(content))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
chosenVersion := getQRCodeVersion(level, version)
|
|
||||||
|
|
||||||
if chosenVersion == nil {
|
|
||||||
return nil, errors.New("cannot find QR Code version")
|
|
||||||
}
|
|
||||||
|
|
||||||
if encoded.Len() > chosenVersion.numDataBits() {
|
|
||||||
return nil, fmt.Errorf("Cannot encode QR code: content too large for fixed size QR Code version %d (encoded length is %d bits, maximum length is %d bits)",
|
|
||||||
version,
|
|
||||||
encoded.Len(),
|
|
||||||
chosenVersion.numDataBits())
|
|
||||||
}
|
|
||||||
|
|
||||||
q := &QRCode{
|
|
||||||
Content: content,
|
|
||||||
|
|
||||||
Level: level,
|
|
||||||
VersionNumber: chosenVersion.version,
|
|
||||||
|
|
||||||
ForegroundColor: color.Black,
|
|
||||||
BackgroundColor: color.White,
|
|
||||||
|
|
||||||
encoder: encoder,
|
|
||||||
data: encoded,
|
|
||||||
version: *chosenVersion,
|
|
||||||
}
|
|
||||||
|
|
||||||
return q, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bitmap returns the QR Code as a 2D array of 1-bit pixels.
|
|
||||||
//
|
|
||||||
// bitmap[y][x] is true if the pixel at (x, y) is set.
|
|
||||||
//
|
|
||||||
// The bitmap includes the required "quiet zone" around the QR Code to aid
|
|
||||||
// decoding.
|
|
||||||
func (q *QRCode) Bitmap() [][]bool {
|
|
||||||
// Build QR code.
|
|
||||||
q.encode()
|
|
||||||
|
|
||||||
return q.symbol.bitmap()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Image returns the QR Code as an image.Image.
|
|
||||||
//
|
|
||||||
// A positive size sets a fixed image width and height (e.g. 256 yields an
|
|
||||||
// 256x256px image).
|
|
||||||
//
|
|
||||||
// Depending on the amount of data encoded, fixed size images can have different
|
|
||||||
// amounts of padding (white space around the QR Code). As an alternative, a
|
|
||||||
// variable sized image can be generated instead:
|
|
||||||
//
|
|
||||||
// A negative size causes a variable sized image to be returned. The image
|
|
||||||
// returned is the minimum size required for the QR Code. Choose a larger
|
|
||||||
// negative number to increase the scale of the image. e.g. a size of -5 causes
|
|
||||||
// each module (QR Code "pixel") to be 5px in size.
|
|
||||||
func (q *QRCode) Image(size int) image.Image {
|
|
||||||
// Build QR code.
|
|
||||||
q.encode()
|
|
||||||
|
|
||||||
// Minimum pixels (both width and height) required.
|
|
||||||
realSize := q.symbol.size
|
|
||||||
|
|
||||||
// Variable size support.
|
|
||||||
if size < 0 {
|
|
||||||
size = size * -1 * realSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Actual pixels available to draw the symbol. Automatically increase the
|
|
||||||
// image size if it's not large enough.
|
|
||||||
if size < realSize {
|
|
||||||
size = realSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output image.
|
|
||||||
rect := image.Rectangle{Min: image.Point{0, 0}, Max: image.Point{size, size}}
|
|
||||||
|
|
||||||
// Saves a few bytes to have them in this order
|
|
||||||
p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor})
|
|
||||||
img := image.NewPaletted(rect, p)
|
|
||||||
fgClr := uint8(img.Palette.Index(q.ForegroundColor))
|
|
||||||
|
|
||||||
// QR code bitmap.
|
|
||||||
bitmap := q.symbol.bitmap()
|
|
||||||
|
|
||||||
// Map each image pixel to the nearest QR code module.
|
|
||||||
modulesPerPixel := float64(realSize) / float64(size)
|
|
||||||
for y := 0; y < size; y++ {
|
|
||||||
y2 := int(float64(y) * modulesPerPixel)
|
|
||||||
for x := 0; x < size; x++ {
|
|
||||||
x2 := int(float64(x) * modulesPerPixel)
|
|
||||||
|
|
||||||
v := bitmap[y2][x2]
|
|
||||||
|
|
||||||
if v {
|
|
||||||
pos := img.PixOffset(x, y)
|
|
||||||
img.Pix[pos] = fgClr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return img
|
|
||||||
}
|
|
||||||
|
|
||||||
// PNG returns the QR Code as a PNG image.
|
|
||||||
//
|
|
||||||
// size is both the image width and height in pixels. If size is too small then
|
|
||||||
// a larger image is silently returned. Negative values for size cause a
|
|
||||||
// variable sized image to be returned: See the documentation for Image().
|
|
||||||
func (q *QRCode) PNG(size int) ([]byte, error) {
|
|
||||||
img := q.Image(size)
|
|
||||||
|
|
||||||
encoder := png.Encoder{CompressionLevel: png.BestCompression}
|
|
||||||
|
|
||||||
var b bytes.Buffer
|
|
||||||
err := encoder.Encode(&b, img)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes the QR Code as a PNG image to io.Writer.
|
|
||||||
//
|
|
||||||
// size is both the image width and height in pixels. If size is too small then
|
|
||||||
// a larger image is silently written. Negative values for size cause a
|
|
||||||
// variable sized image to be written: See the documentation for Image().
|
|
||||||
func (q *QRCode) Write(size int, out io.Writer) error {
|
|
||||||
var png []byte
|
|
||||||
|
|
||||||
png, err := q.PNG(size)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, err = out.Write(png)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// WriteFile writes the QR Code as a PNG image to the specified file.
|
|
||||||
//
|
|
||||||
// size is both the image width and height in pixels. If size is too small then
|
|
||||||
// a larger image is silently written. Negative values for size cause a
|
|
||||||
// variable sized image to be written: See the documentation for Image().
|
|
||||||
func (q *QRCode) WriteFile(size int, filename string) error {
|
|
||||||
var png []byte
|
|
||||||
|
|
||||||
png, err := q.PNG(size)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ioutil.WriteFile(filename, png, os.FileMode(0644))
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode completes the steps required to encode the QR Code. These include
|
|
||||||
// adding the terminator bits and padding, splitting the data into blocks and
|
|
||||||
// applying the error correction, and selecting the best data mask.
|
|
||||||
func (q *QRCode) encode() {
|
|
||||||
numTerminatorBits := q.version.numTerminatorBitsRequired(q.data.Len())
|
|
||||||
|
|
||||||
q.addTerminatorBits(numTerminatorBits)
|
|
||||||
q.addPadding()
|
|
||||||
|
|
||||||
encoded := q.encodeBlocks()
|
|
||||||
|
|
||||||
const numMasks int = 8
|
|
||||||
penalty := 0
|
|
||||||
|
|
||||||
for mask := 0; mask < numMasks; mask++ {
|
|
||||||
var s *symbol
|
|
||||||
var err error
|
|
||||||
|
|
||||||
s, err = buildRegularSymbol(q.version, mask, encoded, !q.DisableBorder)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Panic(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
numEmptyModules := s.numEmptyModules()
|
|
||||||
if numEmptyModules != 0 {
|
|
||||||
log.Panicf("bug: numEmptyModules is %d (expected 0) (version=%d)",
|
|
||||||
numEmptyModules, q.VersionNumber)
|
|
||||||
}
|
|
||||||
|
|
||||||
p := s.penaltyScore()
|
|
||||||
|
|
||||||
//log.Printf("mask=%d p=%3d p1=%3d p2=%3d p3=%3d p4=%d\n", mask, p, s.penalty1(), s.penalty2(), s.penalty3(), s.penalty4())
|
|
||||||
|
|
||||||
if q.symbol == nil || p < penalty {
|
|
||||||
q.symbol = s
|
|
||||||
q.mask = mask
|
|
||||||
penalty = p
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// addTerminatorBits adds final terminator bits to the encoded data.
|
|
||||||
//
|
|
||||||
// The number of terminator bits required is determined when the QR Code version
|
|
||||||
// is chosen (which itself depends on the length of the data encoded). The
|
|
||||||
// terminator bits are thus added after the QR Code version
|
|
||||||
// is chosen, rather than at the data encoding stage.
|
|
||||||
func (q *QRCode) addTerminatorBits(numTerminatorBits int) {
|
|
||||||
q.data.AppendNumBools(numTerminatorBits, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodeBlocks takes the completed (terminated & padded) encoded data, splits
|
|
||||||
// the data into blocks (as specified by the QR Code version), applies error
|
|
||||||
// correction to each block, then interleaves the blocks together.
|
|
||||||
//
|
|
||||||
// The QR Code's final data sequence is returned.
|
|
||||||
func (q *QRCode) encodeBlocks() *bitset.Bitset {
|
|
||||||
// Split into blocks.
|
|
||||||
type dataBlock struct {
|
|
||||||
data *bitset.Bitset
|
|
||||||
ecStartOffset int
|
|
||||||
}
|
|
||||||
|
|
||||||
block := make([]dataBlock, q.version.numBlocks())
|
|
||||||
|
|
||||||
start := 0
|
|
||||||
end := 0
|
|
||||||
blockID := 0
|
|
||||||
|
|
||||||
for _, b := range q.version.block {
|
|
||||||
for j := 0; j < b.numBlocks; j++ {
|
|
||||||
start = end
|
|
||||||
end = start + b.numDataCodewords*8
|
|
||||||
|
|
||||||
// Apply error correction to each block.
|
|
||||||
numErrorCodewords := b.numCodewords - b.numDataCodewords
|
|
||||||
block[blockID].data = reedsolomon.Encode(q.data.Substr(start, end), numErrorCodewords)
|
|
||||||
block[blockID].ecStartOffset = end - start
|
|
||||||
|
|
||||||
blockID++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interleave the blocks.
|
|
||||||
|
|
||||||
result := bitset.New()
|
|
||||||
|
|
||||||
// Combine data blocks.
|
|
||||||
working := true
|
|
||||||
for i := 0; working; i += 8 {
|
|
||||||
working = false
|
|
||||||
|
|
||||||
for j, b := range block {
|
|
||||||
if i >= block[j].ecStartOffset {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Append(b.data.Substr(i, i+8))
|
|
||||||
|
|
||||||
working = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Combine error correction blocks.
|
|
||||||
working = true
|
|
||||||
for i := 0; working; i += 8 {
|
|
||||||
working = false
|
|
||||||
|
|
||||||
for j, b := range block {
|
|
||||||
offset := i + block[j].ecStartOffset
|
|
||||||
if offset >= block[j].data.Len() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Append(b.data.Substr(offset, offset+8))
|
|
||||||
|
|
||||||
working = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append remainder bits.
|
|
||||||
result.AppendNumBools(q.version.numRemainderBits, false)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// max returns the maximum of a and b.
|
|
||||||
func max(a int, b int) int {
|
|
||||||
if a > b {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// addPadding pads the encoded data upto the full length required.
|
|
||||||
func (q *QRCode) addPadding() {
|
|
||||||
numDataBits := q.version.numDataBits()
|
|
||||||
|
|
||||||
if q.data.Len() == numDataBits {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pad to the nearest codeword boundary.
|
|
||||||
q.data.AppendNumBools(q.version.numBitsToPadToCodeword(q.data.Len()), false)
|
|
||||||
|
|
||||||
// Pad codewords 0b11101100 and 0b00010001.
|
|
||||||
padding := [2]*bitset.Bitset{
|
|
||||||
bitset.New(true, true, true, false, true, true, false, false),
|
|
||||||
bitset.New(false, false, false, true, false, false, false, true),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert pad codewords alternately.
|
|
||||||
i := 0
|
|
||||||
for numDataBits-q.data.Len() >= 8 {
|
|
||||||
q.data.Append(padding[i])
|
|
||||||
|
|
||||||
i = 1 - i // Alternate between 0 and 1.
|
|
||||||
}
|
|
||||||
|
|
||||||
if q.data.Len() != numDataBits {
|
|
||||||
log.Panicf("BUG: got len %d, expected %d", q.data.Len(), numDataBits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToString produces a multi-line string that forms a QR-code image.
|
|
||||||
func (q *QRCode) ToString(inverseColor bool) string {
|
|
||||||
bits := q.Bitmap()
|
|
||||||
var buf bytes.Buffer
|
|
||||||
for y := range bits {
|
|
||||||
for x := range bits[y] {
|
|
||||||
if bits[y][x] != inverseColor {
|
|
||||||
buf.WriteString(" ")
|
|
||||||
} else {
|
|
||||||
buf.WriteString("██")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSmallString produces a multi-line string that forms a QR-code image, a
|
|
||||||
// factor two smaller in x and y then ToString.
|
|
||||||
func (q *QRCode) ToSmallString(inverseColor bool) string {
|
|
||||||
bits := q.Bitmap()
|
|
||||||
var buf bytes.Buffer
|
|
||||||
// if there is an odd number of rows, the last one needs special treatment
|
|
||||||
for y := 0; y < len(bits)-1; y += 2 {
|
|
||||||
for x := range bits[y] {
|
|
||||||
if bits[y][x] == bits[y+1][x] {
|
|
||||||
if bits[y][x] != inverseColor {
|
|
||||||
buf.WriteString(" ")
|
|
||||||
} else {
|
|
||||||
buf.WriteString("█")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if bits[y][x] != inverseColor {
|
|
||||||
buf.WriteString("▄")
|
|
||||||
} else {
|
|
||||||
buf.WriteString("▀")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
// special treatment for the last row if odd
|
|
||||||
if len(bits)%2 == 1 {
|
|
||||||
y := len(bits) - 1
|
|
||||||
for x := range bits[y] {
|
|
||||||
if bits[y][x] != inverseColor {
|
|
||||||
buf.WriteString(" ")
|
|
||||||
} else {
|
|
||||||
buf.WriteString("▀")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
generated
vendored
387
vendor/github.com/skip2/go-qrcode/reedsolomon/gf2_8.go
generated
vendored
@ -1,387 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
package reedsolomon
|
|
||||||
|
|
||||||
// Addition, subtraction, multiplication, and division in GF(2^8).
|
|
||||||
// Operations are performed modulo x^8 + x^4 + x^3 + x^2 + 1.
|
|
||||||
|
|
||||||
// http://en.wikipedia.org/wiki/Finite_field_arithmetic
|
|
||||||
|
|
||||||
import "log"
|
|
||||||
|
|
||||||
const (
|
|
||||||
gfZero = gfElement(0)
|
|
||||||
gfOne = gfElement(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
gfExpTable = [256]gfElement{
|
|
||||||
/* 0 - 9 */ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58,
|
|
||||||
/* 10 - 19 */ 116, 232, 205, 135, 19, 38, 76, 152, 45, 90,
|
|
||||||
/* 20 - 29 */ 180, 117, 234, 201, 143, 3, 6, 12, 24, 48,
|
|
||||||
/* 30 - 39 */ 96, 192, 157, 39, 78, 156, 37, 74, 148, 53,
|
|
||||||
/* 40 - 49 */ 106, 212, 181, 119, 238, 193, 159, 35, 70, 140,
|
|
||||||
/* 50 - 59 */ 5, 10, 20, 40, 80, 160, 93, 186, 105, 210,
|
|
||||||
/* 60 - 69 */ 185, 111, 222, 161, 95, 190, 97, 194, 153, 47,
|
|
||||||
/* 70 - 79 */ 94, 188, 101, 202, 137, 15, 30, 60, 120, 240,
|
|
||||||
/* 80 - 89 */ 253, 231, 211, 187, 107, 214, 177, 127, 254, 225,
|
|
||||||
/* 90 - 99 */ 223, 163, 91, 182, 113, 226, 217, 175, 67, 134,
|
|
||||||
/* 100 - 109 */ 17, 34, 68, 136, 13, 26, 52, 104, 208, 189,
|
|
||||||
/* 110 - 119 */ 103, 206, 129, 31, 62, 124, 248, 237, 199, 147,
|
|
||||||
/* 120 - 129 */ 59, 118, 236, 197, 151, 51, 102, 204, 133, 23,
|
|
||||||
/* 130 - 139 */ 46, 92, 184, 109, 218, 169, 79, 158, 33, 66,
|
|
||||||
/* 140 - 149 */ 132, 21, 42, 84, 168, 77, 154, 41, 82, 164,
|
|
||||||
/* 150 - 159 */ 85, 170, 73, 146, 57, 114, 228, 213, 183, 115,
|
|
||||||
/* 160 - 169 */ 230, 209, 191, 99, 198, 145, 63, 126, 252, 229,
|
|
||||||
/* 170 - 179 */ 215, 179, 123, 246, 241, 255, 227, 219, 171, 75,
|
|
||||||
/* 180 - 189 */ 150, 49, 98, 196, 149, 55, 110, 220, 165, 87,
|
|
||||||
/* 190 - 199 */ 174, 65, 130, 25, 50, 100, 200, 141, 7, 14,
|
|
||||||
/* 200 - 209 */ 28, 56, 112, 224, 221, 167, 83, 166, 81, 162,
|
|
||||||
/* 210 - 219 */ 89, 178, 121, 242, 249, 239, 195, 155, 43, 86,
|
|
||||||
/* 220 - 229 */ 172, 69, 138, 9, 18, 36, 72, 144, 61, 122,
|
|
||||||
/* 230 - 239 */ 244, 245, 247, 243, 251, 235, 203, 139, 11, 22,
|
|
||||||
/* 240 - 249 */ 44, 88, 176, 125, 250, 233, 207, 131, 27, 54,
|
|
||||||
/* 250 - 255 */ 108, 216, 173, 71, 142, 1}
|
|
||||||
|
|
||||||
gfLogTable = [256]int{
|
|
||||||
/* 0 - 9 */ -1, 0, 1, 25, 2, 50, 26, 198, 3, 223,
|
|
||||||
/* 10 - 19 */ 51, 238, 27, 104, 199, 75, 4, 100, 224, 14,
|
|
||||||
/* 20 - 29 */ 52, 141, 239, 129, 28, 193, 105, 248, 200, 8,
|
|
||||||
/* 30 - 39 */ 76, 113, 5, 138, 101, 47, 225, 36, 15, 33,
|
|
||||||
/* 40 - 49 */ 53, 147, 142, 218, 240, 18, 130, 69, 29, 181,
|
|
||||||
/* 50 - 59 */ 194, 125, 106, 39, 249, 185, 201, 154, 9, 120,
|
|
||||||
/* 60 - 69 */ 77, 228, 114, 166, 6, 191, 139, 98, 102, 221,
|
|
||||||
/* 70 - 79 */ 48, 253, 226, 152, 37, 179, 16, 145, 34, 136,
|
|
||||||
/* 80 - 89 */ 54, 208, 148, 206, 143, 150, 219, 189, 241, 210,
|
|
||||||
/* 90 - 99 */ 19, 92, 131, 56, 70, 64, 30, 66, 182, 163,
|
|
||||||
/* 100 - 109 */ 195, 72, 126, 110, 107, 58, 40, 84, 250, 133,
|
|
||||||
/* 110 - 119 */ 186, 61, 202, 94, 155, 159, 10, 21, 121, 43,
|
|
||||||
/* 120 - 129 */ 78, 212, 229, 172, 115, 243, 167, 87, 7, 112,
|
|
||||||
/* 130 - 139 */ 192, 247, 140, 128, 99, 13, 103, 74, 222, 237,
|
|
||||||
/* 140 - 149 */ 49, 197, 254, 24, 227, 165, 153, 119, 38, 184,
|
|
||||||
/* 150 - 159 */ 180, 124, 17, 68, 146, 217, 35, 32, 137, 46,
|
|
||||||
/* 160 - 169 */ 55, 63, 209, 91, 149, 188, 207, 205, 144, 135,
|
|
||||||
/* 170 - 179 */ 151, 178, 220, 252, 190, 97, 242, 86, 211, 171,
|
|
||||||
/* 180 - 189 */ 20, 42, 93, 158, 132, 60, 57, 83, 71, 109,
|
|
||||||
/* 190 - 199 */ 65, 162, 31, 45, 67, 216, 183, 123, 164, 118,
|
|
||||||
/* 200 - 209 */ 196, 23, 73, 236, 127, 12, 111, 246, 108, 161,
|
|
||||||
/* 210 - 219 */ 59, 82, 41, 157, 85, 170, 251, 96, 134, 177,
|
|
||||||
/* 220 - 229 */ 187, 204, 62, 90, 203, 89, 95, 176, 156, 169,
|
|
||||||
/* 230 - 239 */ 160, 81, 11, 245, 22, 235, 122, 117, 44, 215,
|
|
||||||
/* 240 - 249 */ 79, 174, 213, 233, 230, 231, 173, 232, 116, 214,
|
|
||||||
/* 250 - 255 */ 244, 234, 168, 80, 88, 175}
|
|
||||||
)
|
|
||||||
|
|
||||||
// gfElement is an element in GF(2^8).
|
|
||||||
type gfElement uint8
|
|
||||||
|
|
||||||
// newGFElement creates and returns a new gfElement.
|
|
||||||
func newGFElement(data byte) gfElement {
|
|
||||||
return gfElement(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfAdd returns a + b.
|
|
||||||
func gfAdd(a, b gfElement) gfElement {
|
|
||||||
return a ^ b
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfSub returns a - b.
|
|
||||||
//
|
|
||||||
// Note addition is equivalent to subtraction in GF(2).
|
|
||||||
func gfSub(a, b gfElement) gfElement {
|
|
||||||
return a ^ b
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfMultiply returns a * b.
|
|
||||||
func gfMultiply(a, b gfElement) gfElement {
|
|
||||||
if a == gfZero || b == gfZero {
|
|
||||||
return gfZero
|
|
||||||
}
|
|
||||||
|
|
||||||
return gfExpTable[(gfLogTable[a]+gfLogTable[b])%255]
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfDivide returns a / b.
|
|
||||||
//
|
|
||||||
// Divide by zero results in a panic.
|
|
||||||
func gfDivide(a, b gfElement) gfElement {
|
|
||||||
if a == gfZero {
|
|
||||||
return gfZero
|
|
||||||
} else if b == gfZero {
|
|
||||||
log.Panicln("Divide by zero")
|
|
||||||
}
|
|
||||||
|
|
||||||
return gfMultiply(a, gfInverse(b))
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfInverse returns the multiplicative inverse of a, a^-1.
|
|
||||||
//
|
|
||||||
// a * a^-1 = 1
|
|
||||||
func gfInverse(a gfElement) gfElement {
|
|
||||||
if a == gfZero {
|
|
||||||
log.Panicln("No multiplicative inverse of 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
return gfExpTable[255-gfLogTable[a]]
|
|
||||||
}
|
|
||||||
|
|
||||||
// a^i | bits | polynomial | decimal
|
|
||||||
// --------------------------------------------------------------------------
|
|
||||||
// 0 | 000000000 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 0
|
|
||||||
// a^0 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
|
|
||||||
// a^1 | 000000010 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 2
|
|
||||||
// a^2 | 000000100 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 4
|
|
||||||
// a^3 | 000001000 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 8
|
|
||||||
// a^4 | 000010000 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 16
|
|
||||||
// a^5 | 000100000 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 32
|
|
||||||
// a^6 | 001000000 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 64
|
|
||||||
// a^7 | 010000000 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 128
|
|
||||||
// a^8 | 000011101 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 29
|
|
||||||
// a^9 | 000111010 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 58
|
|
||||||
// a^10 | 001110100 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 116
|
|
||||||
// a^11 | 011101000 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 232
|
|
||||||
// a^12 | 011001101 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 205
|
|
||||||
// a^13 | 010000111 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 135
|
|
||||||
// a^14 | 000010011 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 19
|
|
||||||
// a^15 | 000100110 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 38
|
|
||||||
// a^16 | 001001100 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 76
|
|
||||||
// a^17 | 010011000 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 152
|
|
||||||
// a^18 | 000101101 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 45
|
|
||||||
// a^19 | 001011010 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 90
|
|
||||||
// a^20 | 010110100 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 180
|
|
||||||
// a^21 | 001110101 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 117
|
|
||||||
// a^22 | 011101010 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 234
|
|
||||||
// a^23 | 011001001 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 201
|
|
||||||
// a^24 | 010001111 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 143
|
|
||||||
// a^25 | 000000011 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 3
|
|
||||||
// a^26 | 000000110 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 6
|
|
||||||
// a^27 | 000001100 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 12
|
|
||||||
// a^28 | 000011000 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 24
|
|
||||||
// a^29 | 000110000 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 48
|
|
||||||
// a^30 | 001100000 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 96
|
|
||||||
// a^31 | 011000000 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 192
|
|
||||||
// a^32 | 010011101 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 157
|
|
||||||
// a^33 | 000100111 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 39
|
|
||||||
// a^34 | 001001110 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 78
|
|
||||||
// a^35 | 010011100 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 156
|
|
||||||
// a^36 | 000100101 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 37
|
|
||||||
// a^37 | 001001010 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 74
|
|
||||||
// a^38 | 010010100 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 148
|
|
||||||
// a^39 | 000110101 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 53
|
|
||||||
// a^40 | 001101010 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 106
|
|
||||||
// a^41 | 011010100 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 212
|
|
||||||
// a^42 | 010110101 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 181
|
|
||||||
// a^43 | 001110111 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 119
|
|
||||||
// a^44 | 011101110 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 238
|
|
||||||
// a^45 | 011000001 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 193
|
|
||||||
// a^46 | 010011111 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 159
|
|
||||||
// a^47 | 000100011 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 35
|
|
||||||
// a^48 | 001000110 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 70
|
|
||||||
// a^49 | 010001100 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 140
|
|
||||||
// a^50 | 000000101 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 5
|
|
||||||
// a^51 | 000001010 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 10
|
|
||||||
// a^52 | 000010100 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 20
|
|
||||||
// a^53 | 000101000 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 40
|
|
||||||
// a^54 | 001010000 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 80
|
|
||||||
// a^55 | 010100000 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 160
|
|
||||||
// a^56 | 001011101 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 93
|
|
||||||
// a^57 | 010111010 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 186
|
|
||||||
// a^58 | 001101001 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 105
|
|
||||||
// a^59 | 011010010 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 210
|
|
||||||
// a^60 | 010111001 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 185
|
|
||||||
// a^61 | 001101111 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 111
|
|
||||||
// a^62 | 011011110 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 222
|
|
||||||
// a^63 | 010100001 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 161
|
|
||||||
// a^64 | 001011111 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 95
|
|
||||||
// a^65 | 010111110 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 190
|
|
||||||
// a^66 | 001100001 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 97
|
|
||||||
// a^67 | 011000010 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 194
|
|
||||||
// a^68 | 010011001 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 153
|
|
||||||
// a^69 | 000101111 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 47
|
|
||||||
// a^70 | 001011110 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 94
|
|
||||||
// a^71 | 010111100 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 188
|
|
||||||
// a^72 | 001100101 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 101
|
|
||||||
// a^73 | 011001010 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 202
|
|
||||||
// a^74 | 010001001 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 137
|
|
||||||
// a^75 | 000001111 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 15
|
|
||||||
// a^76 | 000011110 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 30
|
|
||||||
// a^77 | 000111100 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 60
|
|
||||||
// a^78 | 001111000 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 120
|
|
||||||
// a^79 | 011110000 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 240
|
|
||||||
// a^80 | 011111101 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 253
|
|
||||||
// a^81 | 011100111 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 231
|
|
||||||
// a^82 | 011010011 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 211
|
|
||||||
// a^83 | 010111011 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 187
|
|
||||||
// a^84 | 001101011 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 107
|
|
||||||
// a^85 | 011010110 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 214
|
|
||||||
// a^86 | 010110001 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 177
|
|
||||||
// a^87 | 001111111 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 127
|
|
||||||
// a^88 | 011111110 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 254
|
|
||||||
// a^89 | 011100001 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 225
|
|
||||||
// a^90 | 011011111 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 223
|
|
||||||
// a^91 | 010100011 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 163
|
|
||||||
// a^92 | 001011011 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 91
|
|
||||||
// a^93 | 010110110 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 182
|
|
||||||
// a^94 | 001110001 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 113
|
|
||||||
// a^95 | 011100010 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 226
|
|
||||||
// a^96 | 011011001 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 217
|
|
||||||
// a^97 | 010101111 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 175
|
|
||||||
// a^98 | 001000011 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 67
|
|
||||||
// a^99 | 010000110 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 134
|
|
||||||
// a^100 | 000010001 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 17
|
|
||||||
// a^101 | 000100010 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 34
|
|
||||||
// a^102 | 001000100 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 68
|
|
||||||
// a^103 | 010001000 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 136
|
|
||||||
// a^104 | 000001101 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 13
|
|
||||||
// a^105 | 000011010 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 26
|
|
||||||
// a^106 | 000110100 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 52
|
|
||||||
// a^107 | 001101000 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 104
|
|
||||||
// a^108 | 011010000 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 208
|
|
||||||
// a^109 | 010111101 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 189
|
|
||||||
// a^110 | 001100111 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 103
|
|
||||||
// a^111 | 011001110 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 206
|
|
||||||
// a^112 | 010000001 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 129
|
|
||||||
// a^113 | 000011111 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 31
|
|
||||||
// a^114 | 000111110 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 62
|
|
||||||
// a^115 | 001111100 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 124
|
|
||||||
// a^116 | 011111000 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 248
|
|
||||||
// a^117 | 011101101 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 237
|
|
||||||
// a^118 | 011000111 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 199
|
|
||||||
// a^119 | 010010011 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 147
|
|
||||||
// a^120 | 000111011 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 59
|
|
||||||
// a^121 | 001110110 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 118
|
|
||||||
// a^122 | 011101100 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 236
|
|
||||||
// a^123 | 011000101 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 197
|
|
||||||
// a^124 | 010010111 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 151
|
|
||||||
// a^125 | 000110011 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 51
|
|
||||||
// a^126 | 001100110 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 102
|
|
||||||
// a^127 | 011001100 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 204
|
|
||||||
// a^128 | 010000101 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 133
|
|
||||||
// a^129 | 000010111 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 23
|
|
||||||
// a^130 | 000101110 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 46
|
|
||||||
// a^131 | 001011100 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 92
|
|
||||||
// a^132 | 010111000 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 184
|
|
||||||
// a^133 | 001101101 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 109
|
|
||||||
// a^134 | 011011010 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 218
|
|
||||||
// a^135 | 010101001 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 169
|
|
||||||
// a^136 | 001001111 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 79
|
|
||||||
// a^137 | 010011110 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 158
|
|
||||||
// a^138 | 000100001 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 33
|
|
||||||
// a^139 | 001000010 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 66
|
|
||||||
// a^140 | 010000100 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 132
|
|
||||||
// a^141 | 000010101 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 21
|
|
||||||
// a^142 | 000101010 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 42
|
|
||||||
// a^143 | 001010100 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 84
|
|
||||||
// a^144 | 010101000 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 168
|
|
||||||
// a^145 | 001001101 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 77
|
|
||||||
// a^146 | 010011010 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 154
|
|
||||||
// a^147 | 000101001 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 41
|
|
||||||
// a^148 | 001010010 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 82
|
|
||||||
// a^149 | 010100100 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 164
|
|
||||||
// a^150 | 001010101 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 85
|
|
||||||
// a^151 | 010101010 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 170
|
|
||||||
// a^152 | 001001001 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 73
|
|
||||||
// a^153 | 010010010 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 146
|
|
||||||
// a^154 | 000111001 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 57
|
|
||||||
// a^155 | 001110010 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 114
|
|
||||||
// a^156 | 011100100 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 228
|
|
||||||
// a^157 | 011010101 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 213
|
|
||||||
// a^158 | 010110111 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 183
|
|
||||||
// a^159 | 001110011 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 115
|
|
||||||
// a^160 | 011100110 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 230
|
|
||||||
// a^161 | 011010001 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 209
|
|
||||||
// a^162 | 010111111 | 0x^8 1x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 191
|
|
||||||
// a^163 | 001100011 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 99
|
|
||||||
// a^164 | 011000110 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 198
|
|
||||||
// a^165 | 010010001 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 145
|
|
||||||
// a^166 | 000111111 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 63
|
|
||||||
// a^167 | 001111110 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 0x^0 | 126
|
|
||||||
// a^168 | 011111100 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 252
|
|
||||||
// a^169 | 011100101 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 229
|
|
||||||
// a^170 | 011010111 | 0x^8 1x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 215
|
|
||||||
// a^171 | 010110011 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 179
|
|
||||||
// a^172 | 001111011 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 123
|
|
||||||
// a^173 | 011110110 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 246
|
|
||||||
// a^174 | 011110001 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 241
|
|
||||||
// a^175 | 011111111 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 1x^2 1x^1 1x^0 | 255
|
|
||||||
// a^176 | 011100011 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 227
|
|
||||||
// a^177 | 011011011 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 219
|
|
||||||
// a^178 | 010101011 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 171
|
|
||||||
// a^179 | 001001011 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 75
|
|
||||||
// a^180 | 010010110 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 150
|
|
||||||
// a^181 | 000110001 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 49
|
|
||||||
// a^182 | 001100010 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 98
|
|
||||||
// a^183 | 011000100 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 196
|
|
||||||
// a^184 | 010010101 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 149
|
|
||||||
// a^185 | 000110111 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 55
|
|
||||||
// a^186 | 001101110 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 110
|
|
||||||
// a^187 | 011011100 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 220
|
|
||||||
// a^188 | 010100101 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 165
|
|
||||||
// a^189 | 001010111 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 87
|
|
||||||
// a^190 | 010101110 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 174
|
|
||||||
// a^191 | 001000001 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 65
|
|
||||||
// a^192 | 010000010 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 130
|
|
||||||
// a^193 | 000011001 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 25
|
|
||||||
// a^194 | 000110010 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 50
|
|
||||||
// a^195 | 001100100 | 0x^8 0x^7 1x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 100
|
|
||||||
// a^196 | 011001000 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 200
|
|
||||||
// a^197 | 010001101 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 141
|
|
||||||
// a^198 | 000000111 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 7
|
|
||||||
// a^199 | 000001110 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 14
|
|
||||||
// a^200 | 000011100 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 1x^2 0x^1 0x^0 | 28
|
|
||||||
// a^201 | 000111000 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 56
|
|
||||||
// a^202 | 001110000 | 0x^8 0x^7 1x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 112
|
|
||||||
// a^203 | 011100000 | 0x^8 1x^7 1x^6 1x^5 0x^4 0x^3 0x^2 0x^1 0x^0 | 224
|
|
||||||
// a^204 | 011011101 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 221
|
|
||||||
// a^205 | 010100111 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 167
|
|
||||||
// a^206 | 001010011 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 83
|
|
||||||
// a^207 | 010100110 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 1x^2 1x^1 0x^0 | 166
|
|
||||||
// a^208 | 001010001 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 0x^2 0x^1 1x^0 | 81
|
|
||||||
// a^209 | 010100010 | 0x^8 1x^7 0x^6 1x^5 0x^4 0x^3 0x^2 1x^1 0x^0 | 162
|
|
||||||
// a^210 | 001011001 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 89
|
|
||||||
// a^211 | 010110010 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 178
|
|
||||||
// a^212 | 001111001 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 121
|
|
||||||
// a^213 | 011110010 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 242
|
|
||||||
// a^214 | 011111001 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 0x^1 1x^0 | 249
|
|
||||||
// a^215 | 011101111 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 239
|
|
||||||
// a^216 | 011000011 | 0x^8 1x^7 1x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 195
|
|
||||||
// a^217 | 010011011 | 0x^8 1x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 155
|
|
||||||
// a^218 | 000101011 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 43
|
|
||||||
// a^219 | 001010110 | 0x^8 0x^7 1x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 86
|
|
||||||
// a^220 | 010101100 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 172
|
|
||||||
// a^221 | 001000101 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 0x^1 1x^0 | 69
|
|
||||||
// a^222 | 010001010 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 0x^0 | 138
|
|
||||||
// a^223 | 000001001 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 9
|
|
||||||
// a^224 | 000010010 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 0x^2 1x^1 0x^0 | 18
|
|
||||||
// a^225 | 000100100 | 0x^8 0x^7 0x^6 1x^5 0x^4 0x^3 1x^2 0x^1 0x^0 | 36
|
|
||||||
// a^226 | 001001000 | 0x^8 0x^7 1x^6 0x^5 0x^4 1x^3 0x^2 0x^1 0x^0 | 72
|
|
||||||
// a^227 | 010010000 | 0x^8 1x^7 0x^6 0x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 144
|
|
||||||
// a^228 | 000111101 | 0x^8 0x^7 0x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 61
|
|
||||||
// a^229 | 001111010 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 122
|
|
||||||
// a^230 | 011110100 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 0x^0 | 244
|
|
||||||
// a^231 | 011110101 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 0x^1 1x^0 | 245
|
|
||||||
// a^232 | 011110111 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 1x^2 1x^1 1x^0 | 247
|
|
||||||
// a^233 | 011110011 | 0x^8 1x^7 1x^6 1x^5 1x^4 0x^3 0x^2 1x^1 1x^0 | 243
|
|
||||||
// a^234 | 011111011 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 251
|
|
||||||
// a^235 | 011101011 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 235
|
|
||||||
// a^236 | 011001011 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 203
|
|
||||||
// a^237 | 010001011 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 139
|
|
||||||
// a^238 | 000001011 | 0x^8 0x^7 0x^6 0x^5 0x^4 1x^3 0x^2 1x^1 1x^0 | 11
|
|
||||||
// a^239 | 000010110 | 0x^8 0x^7 0x^6 0x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 22
|
|
||||||
// a^240 | 000101100 | 0x^8 0x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 44
|
|
||||||
// a^241 | 001011000 | 0x^8 0x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 88
|
|
||||||
// a^242 | 010110000 | 0x^8 1x^7 0x^6 1x^5 1x^4 0x^3 0x^2 0x^1 0x^0 | 176
|
|
||||||
// a^243 | 001111101 | 0x^8 0x^7 1x^6 1x^5 1x^4 1x^3 1x^2 0x^1 1x^0 | 125
|
|
||||||
// a^244 | 011111010 | 0x^8 1x^7 1x^6 1x^5 1x^4 1x^3 0x^2 1x^1 0x^0 | 250
|
|
||||||
// a^245 | 011101001 | 0x^8 1x^7 1x^6 1x^5 0x^4 1x^3 0x^2 0x^1 1x^0 | 233
|
|
||||||
// a^246 | 011001111 | 0x^8 1x^7 1x^6 0x^5 0x^4 1x^3 1x^2 1x^1 1x^0 | 207
|
|
||||||
// a^247 | 010000011 | 0x^8 1x^7 0x^6 0x^5 0x^4 0x^3 0x^2 1x^1 1x^0 | 131
|
|
||||||
// a^248 | 000011011 | 0x^8 0x^7 0x^6 0x^5 1x^4 1x^3 0x^2 1x^1 1x^0 | 27
|
|
||||||
// a^249 | 000110110 | 0x^8 0x^7 0x^6 1x^5 1x^4 0x^3 1x^2 1x^1 0x^0 | 54
|
|
||||||
// a^250 | 001101100 | 0x^8 0x^7 1x^6 1x^5 0x^4 1x^3 1x^2 0x^1 0x^0 | 108
|
|
||||||
// a^251 | 011011000 | 0x^8 1x^7 1x^6 0x^5 1x^4 1x^3 0x^2 0x^1 0x^0 | 216
|
|
||||||
// a^252 | 010101101 | 0x^8 1x^7 0x^6 1x^5 0x^4 1x^3 1x^2 0x^1 1x^0 | 173
|
|
||||||
// a^253 | 001000111 | 0x^8 0x^7 1x^6 0x^5 0x^4 0x^3 1x^2 1x^1 1x^0 | 71
|
|
||||||
// a^254 | 010001110 | 0x^8 1x^7 0x^6 0x^5 0x^4 1x^3 1x^2 1x^1 0x^0 | 142
|
|
||||||
// a^255 | 000000001 | 0x^8 0x^7 0x^6 0x^5 0x^4 0x^3 0x^2 0x^1 1x^0 | 1
|
|
216
vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
generated
vendored
216
vendor/github.com/skip2/go-qrcode/reedsolomon/gf_poly.go
generated
vendored
@ -1,216 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
package reedsolomon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
bitset "github.com/skip2/go-qrcode/bitset"
|
|
||||||
)
|
|
||||||
|
|
||||||
// gfPoly is a polynomial over GF(2^8).
|
|
||||||
type gfPoly struct {
|
|
||||||
// The ith value is the coefficient of the ith degree of x.
|
|
||||||
// term[0]*(x^0) + term[1]*(x^1) + term[2]*(x^2) ...
|
|
||||||
term []gfElement
|
|
||||||
}
|
|
||||||
|
|
||||||
// newGFPolyFromData returns |data| as a polynomial over GF(2^8).
|
|
||||||
//
|
|
||||||
// Each data byte becomes the coefficient of an x term.
|
|
||||||
//
|
|
||||||
// For an n byte input the polynomial is:
|
|
||||||
// data[n-1]*(x^n-1) + data[n-2]*(x^n-2) ... + data[0]*(x^0).
|
|
||||||
func newGFPolyFromData(data *bitset.Bitset) gfPoly {
|
|
||||||
numTotalBytes := data.Len() / 8
|
|
||||||
if data.Len()%8 != 0 {
|
|
||||||
numTotalBytes++
|
|
||||||
}
|
|
||||||
|
|
||||||
result := gfPoly{term: make([]gfElement, numTotalBytes)}
|
|
||||||
|
|
||||||
i := numTotalBytes - 1
|
|
||||||
for j := 0; j < data.Len(); j += 8 {
|
|
||||||
result.term[i] = gfElement(data.ByteAt(j))
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// newGFPolyMonomial returns term*(x^degree).
|
|
||||||
func newGFPolyMonomial(term gfElement, degree int) gfPoly {
|
|
||||||
if term == gfZero {
|
|
||||||
return gfPoly{}
|
|
||||||
}
|
|
||||||
|
|
||||||
result := gfPoly{term: make([]gfElement, degree+1)}
|
|
||||||
result.term[degree] = term
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e gfPoly) data(numTerms int) []byte {
|
|
||||||
result := make([]byte, numTerms)
|
|
||||||
|
|
||||||
i := numTerms - len(e.term)
|
|
||||||
for j := len(e.term) - 1; j >= 0; j-- {
|
|
||||||
result[i] = byte(e.term[j])
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// numTerms returns the number of
|
|
||||||
func (e gfPoly) numTerms() int {
|
|
||||||
return len(e.term)
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfPolyMultiply returns a * b.
|
|
||||||
func gfPolyMultiply(a, b gfPoly) gfPoly {
|
|
||||||
numATerms := a.numTerms()
|
|
||||||
numBTerms := b.numTerms()
|
|
||||||
|
|
||||||
result := gfPoly{term: make([]gfElement, numATerms+numBTerms)}
|
|
||||||
|
|
||||||
for i := 0; i < numATerms; i++ {
|
|
||||||
for j := 0; j < numBTerms; j++ {
|
|
||||||
if a.term[i] != 0 && b.term[j] != 0 {
|
|
||||||
monomial := gfPoly{term: make([]gfElement, i+j+1)}
|
|
||||||
monomial.term[i+j] = gfMultiply(a.term[i], b.term[j])
|
|
||||||
|
|
||||||
result = gfPolyAdd(result, monomial)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.normalised()
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfPolyRemainder return the remainder of numerator / denominator.
|
|
||||||
func gfPolyRemainder(numerator, denominator gfPoly) gfPoly {
|
|
||||||
if denominator.equals(gfPoly{}) {
|
|
||||||
log.Panicln("Remainder by zero")
|
|
||||||
}
|
|
||||||
|
|
||||||
remainder := numerator
|
|
||||||
|
|
||||||
for remainder.numTerms() >= denominator.numTerms() {
|
|
||||||
degree := remainder.numTerms() - denominator.numTerms()
|
|
||||||
coefficient := gfDivide(remainder.term[remainder.numTerms()-1],
|
|
||||||
denominator.term[denominator.numTerms()-1])
|
|
||||||
|
|
||||||
divisor := gfPolyMultiply(denominator,
|
|
||||||
newGFPolyMonomial(coefficient, degree))
|
|
||||||
|
|
||||||
remainder = gfPolyAdd(remainder, divisor)
|
|
||||||
}
|
|
||||||
|
|
||||||
return remainder.normalised()
|
|
||||||
}
|
|
||||||
|
|
||||||
// gfPolyAdd returns a + b.
|
|
||||||
func gfPolyAdd(a, b gfPoly) gfPoly {
|
|
||||||
numATerms := a.numTerms()
|
|
||||||
numBTerms := b.numTerms()
|
|
||||||
|
|
||||||
numTerms := numATerms
|
|
||||||
if numBTerms > numTerms {
|
|
||||||
numTerms = numBTerms
|
|
||||||
}
|
|
||||||
|
|
||||||
result := gfPoly{term: make([]gfElement, numTerms)}
|
|
||||||
|
|
||||||
for i := 0; i < numTerms; i++ {
|
|
||||||
switch {
|
|
||||||
case numATerms > i && numBTerms > i:
|
|
||||||
result.term[i] = gfAdd(a.term[i], b.term[i])
|
|
||||||
case numATerms > i:
|
|
||||||
result.term[i] = a.term[i]
|
|
||||||
default:
|
|
||||||
result.term[i] = b.term[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.normalised()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e gfPoly) normalised() gfPoly {
|
|
||||||
numTerms := e.numTerms()
|
|
||||||
maxNonzeroTerm := numTerms - 1
|
|
||||||
|
|
||||||
for i := numTerms - 1; i >= 0; i-- {
|
|
||||||
if e.term[i] != 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
maxNonzeroTerm = i - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if maxNonzeroTerm < 0 {
|
|
||||||
return gfPoly{}
|
|
||||||
} else if maxNonzeroTerm < numTerms-1 {
|
|
||||||
e.term = e.term[0 : maxNonzeroTerm+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e gfPoly) string(useIndexForm bool) string {
|
|
||||||
var str string
|
|
||||||
numTerms := e.numTerms()
|
|
||||||
|
|
||||||
for i := numTerms - 1; i >= 0; i-- {
|
|
||||||
if e.term[i] > 0 {
|
|
||||||
if len(str) > 0 {
|
|
||||||
str += " + "
|
|
||||||
}
|
|
||||||
|
|
||||||
if !useIndexForm {
|
|
||||||
str += fmt.Sprintf("%dx^%d", e.term[i], i)
|
|
||||||
} else {
|
|
||||||
str += fmt.Sprintf("a^%dx^%d", gfLogTable[e.term[i]], i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(str) == 0 {
|
|
||||||
str = "0"
|
|
||||||
}
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
// equals returns true if e == other.
|
|
||||||
func (e gfPoly) equals(other gfPoly) bool {
|
|
||||||
var minecPoly *gfPoly
|
|
||||||
var maxecPoly *gfPoly
|
|
||||||
|
|
||||||
if e.numTerms() > other.numTerms() {
|
|
||||||
minecPoly = &other
|
|
||||||
maxecPoly = &e
|
|
||||||
} else {
|
|
||||||
minecPoly = &e
|
|
||||||
maxecPoly = &other
|
|
||||||
}
|
|
||||||
|
|
||||||
numMinTerms := minecPoly.numTerms()
|
|
||||||
numMaxTerms := maxecPoly.numTerms()
|
|
||||||
|
|
||||||
for i := 0; i < numMinTerms; i++ {
|
|
||||||
if e.term[i] != other.term[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := numMinTerms; i < numMaxTerms; i++ {
|
|
||||||
if maxecPoly.term[i] != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
73
vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
generated
vendored
73
vendor/github.com/skip2/go-qrcode/reedsolomon/reed_solomon.go
generated
vendored
@ -1,73 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
// Package reedsolomon provides error correction encoding for QR Code 2005.
|
|
||||||
//
|
|
||||||
// QR Code 2005 uses a Reed-Solomon error correcting code to detect and correct
|
|
||||||
// errors encountered during decoding.
|
|
||||||
//
|
|
||||||
// The generated RS codes are systematic, and consist of the input data with
|
|
||||||
// error correction bytes appended.
|
|
||||||
package reedsolomon
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
bitset "github.com/skip2/go-qrcode/bitset"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encode data for QR Code 2005 using the appropriate Reed-Solomon code.
|
|
||||||
//
|
|
||||||
// numECBytes is the number of error correction bytes to append, and is
|
|
||||||
// determined by the target QR Code's version and error correction level.
|
|
||||||
//
|
|
||||||
// ISO/IEC 18004 table 9 specifies the numECBytes required. e.g. a 1-L code has
|
|
||||||
// numECBytes=7.
|
|
||||||
func Encode(data *bitset.Bitset, numECBytes int) *bitset.Bitset {
|
|
||||||
// Create a polynomial representing |data|.
|
|
||||||
//
|
|
||||||
// The bytes are interpreted as the sequence of coefficients of a polynomial.
|
|
||||||
// The last byte's value becomes the x^0 coefficient, the second to last
|
|
||||||
// becomes the x^1 coefficient and so on.
|
|
||||||
ecpoly := newGFPolyFromData(data)
|
|
||||||
ecpoly = gfPolyMultiply(ecpoly, newGFPolyMonomial(gfOne, numECBytes))
|
|
||||||
|
|
||||||
// Pick the generator polynomial.
|
|
||||||
generator := rsGeneratorPoly(numECBytes)
|
|
||||||
|
|
||||||
// Generate the error correction bytes.
|
|
||||||
remainder := gfPolyRemainder(ecpoly, generator)
|
|
||||||
|
|
||||||
// Combine the data & error correcting bytes.
|
|
||||||
// The mathematically correct answer is:
|
|
||||||
//
|
|
||||||
// result := gfPolyAdd(ecpoly, remainder).
|
|
||||||
//
|
|
||||||
// The encoding used by QR Code 2005 is slightly different this result: To
|
|
||||||
// preserve the original |data| bit sequence exactly, the data and remainder
|
|
||||||
// are combined manually below. This ensures any most significant zero bits
|
|
||||||
// are preserved (and not optimised away).
|
|
||||||
result := bitset.Clone(data)
|
|
||||||
result.AppendBytes(remainder.data(numECBytes))
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// rsGeneratorPoly returns the Reed-Solomon generator polynomial with |degree|.
|
|
||||||
//
|
|
||||||
// The generator polynomial is calculated as:
|
|
||||||
// (x + a^0)(x + a^1)...(x + a^degree-1)
|
|
||||||
func rsGeneratorPoly(degree int) gfPoly {
|
|
||||||
if degree < 2 {
|
|
||||||
log.Panic("degree < 2")
|
|
||||||
}
|
|
||||||
|
|
||||||
generator := gfPoly{term: []gfElement{1}}
|
|
||||||
|
|
||||||
for i := 0; i < degree; i++ {
|
|
||||||
nextPoly := gfPoly{term: []gfElement{gfExpTable[i], 1}}
|
|
||||||
generator = gfPolyMultiply(generator, nextPoly)
|
|
||||||
}
|
|
||||||
|
|
||||||
return generator
|
|
||||||
}
|
|
315
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
315
vendor/github.com/skip2/go-qrcode/regular_symbol.go
generated
vendored
@ -1,315 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
package qrcode
|
|
||||||
|
|
||||||
import (
|
|
||||||
bitset "github.com/skip2/go-qrcode/bitset"
|
|
||||||
)
|
|
||||||
|
|
||||||
type regularSymbol struct {
|
|
||||||
version qrCodeVersion
|
|
||||||
mask int
|
|
||||||
|
|
||||||
data *bitset.Bitset
|
|
||||||
|
|
||||||
symbol *symbol
|
|
||||||
size int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abbreviated true/false.
|
|
||||||
const (
|
|
||||||
b0 = false
|
|
||||||
b1 = true
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
alignmentPatternCenter = [][]int{
|
|
||||||
{}, // Version 0 doesn't exist.
|
|
||||||
{}, // Version 1 doesn't use alignment patterns.
|
|
||||||
{6, 18},
|
|
||||||
{6, 22},
|
|
||||||
{6, 26},
|
|
||||||
{6, 30},
|
|
||||||
{6, 34},
|
|
||||||
{6, 22, 38},
|
|
||||||
{6, 24, 42},
|
|
||||||
{6, 26, 46},
|
|
||||||
{6, 28, 50},
|
|
||||||
{6, 30, 54},
|
|
||||||
{6, 32, 58},
|
|
||||||
{6, 34, 62},
|
|
||||||
{6, 26, 46, 66},
|
|
||||||
{6, 26, 48, 70},
|
|
||||||
{6, 26, 50, 74},
|
|
||||||
{6, 30, 54, 78},
|
|
||||||
{6, 30, 56, 82},
|
|
||||||
{6, 30, 58, 86},
|
|
||||||
{6, 34, 62, 90},
|
|
||||||
{6, 28, 50, 72, 94},
|
|
||||||
{6, 26, 50, 74, 98},
|
|
||||||
{6, 30, 54, 78, 102},
|
|
||||||
{6, 28, 54, 80, 106},
|
|
||||||
{6, 32, 58, 84, 110},
|
|
||||||
{6, 30, 58, 86, 114},
|
|
||||||
{6, 34, 62, 90, 118},
|
|
||||||
{6, 26, 50, 74, 98, 122},
|
|
||||||
{6, 30, 54, 78, 102, 126},
|
|
||||||
{6, 26, 52, 78, 104, 130},
|
|
||||||
{6, 30, 56, 82, 108, 134},
|
|
||||||
{6, 34, 60, 86, 112, 138},
|
|
||||||
{6, 30, 58, 86, 114, 142},
|
|
||||||
{6, 34, 62, 90, 118, 146},
|
|
||||||
{6, 30, 54, 78, 102, 126, 150},
|
|
||||||
{6, 24, 50, 76, 102, 128, 154},
|
|
||||||
{6, 28, 54, 80, 106, 132, 158},
|
|
||||||
{6, 32, 58, 84, 110, 136, 162},
|
|
||||||
{6, 26, 54, 82, 110, 138, 166},
|
|
||||||
{6, 30, 58, 86, 114, 142, 170},
|
|
||||||
}
|
|
||||||
|
|
||||||
finderPattern = [][]bool{
|
|
||||||
{b1, b1, b1, b1, b1, b1, b1},
|
|
||||||
{b1, b0, b0, b0, b0, b0, b1},
|
|
||||||
{b1, b0, b1, b1, b1, b0, b1},
|
|
||||||
{b1, b0, b1, b1, b1, b0, b1},
|
|
||||||
{b1, b0, b1, b1, b1, b0, b1},
|
|
||||||
{b1, b0, b0, b0, b0, b0, b1},
|
|
||||||
{b1, b1, b1, b1, b1, b1, b1},
|
|
||||||
}
|
|
||||||
|
|
||||||
finderPatternSize = 7
|
|
||||||
|
|
||||||
finderPatternHorizontalBorder = [][]bool{
|
|
||||||
{b0, b0, b0, b0, b0, b0, b0, b0},
|
|
||||||
}
|
|
||||||
|
|
||||||
finderPatternVerticalBorder = [][]bool{
|
|
||||||
{b0},
|
|
||||||
{b0},
|
|
||||||
{b0},
|
|
||||||
{b0},
|
|
||||||
{b0},
|
|
||||||
{b0},
|
|
||||||
{b0},
|
|
||||||
{b0},
|
|
||||||
}
|
|
||||||
|
|
||||||
alignmentPattern = [][]bool{
|
|
||||||
{b1, b1, b1, b1, b1},
|
|
||||||
{b1, b0, b0, b0, b1},
|
|
||||||
{b1, b0, b1, b0, b1},
|
|
||||||
{b1, b0, b0, b0, b1},
|
|
||||||
{b1, b1, b1, b1, b1},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func buildRegularSymbol(version qrCodeVersion, mask int,
|
|
||||||
data *bitset.Bitset, includeQuietZone bool) (*symbol, error) {
|
|
||||||
|
|
||||||
quietZoneSize := 0
|
|
||||||
if includeQuietZone {
|
|
||||||
quietZoneSize = version.quietZoneSize()
|
|
||||||
}
|
|
||||||
|
|
||||||
m := ®ularSymbol{
|
|
||||||
version: version,
|
|
||||||
mask: mask,
|
|
||||||
data: data,
|
|
||||||
|
|
||||||
symbol: newSymbol(version.symbolSize(), quietZoneSize),
|
|
||||||
size: version.symbolSize(),
|
|
||||||
}
|
|
||||||
|
|
||||||
m.addFinderPatterns()
|
|
||||||
m.addAlignmentPatterns()
|
|
||||||
m.addTimingPatterns()
|
|
||||||
m.addFormatInfo()
|
|
||||||
m.addVersionInfo()
|
|
||||||
|
|
||||||
ok, err := m.addData()
|
|
||||||
if !ok {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.symbol, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *regularSymbol) addFinderPatterns() {
|
|
||||||
fpSize := finderPatternSize
|
|
||||||
fp := finderPattern
|
|
||||||
fpHBorder := finderPatternHorizontalBorder
|
|
||||||
fpVBorder := finderPatternVerticalBorder
|
|
||||||
|
|
||||||
// Top left Finder Pattern.
|
|
||||||
m.symbol.set2dPattern(0, 0, fp)
|
|
||||||
m.symbol.set2dPattern(0, fpSize, fpHBorder)
|
|
||||||
m.symbol.set2dPattern(fpSize, 0, fpVBorder)
|
|
||||||
|
|
||||||
// Top right Finder Pattern.
|
|
||||||
m.symbol.set2dPattern(m.size-fpSize, 0, fp)
|
|
||||||
m.symbol.set2dPattern(m.size-fpSize-1, fpSize, fpHBorder)
|
|
||||||
m.symbol.set2dPattern(m.size-fpSize-1, 0, fpVBorder)
|
|
||||||
|
|
||||||
// Bottom left Finder Pattern.
|
|
||||||
m.symbol.set2dPattern(0, m.size-fpSize, fp)
|
|
||||||
m.symbol.set2dPattern(0, m.size-fpSize-1, fpHBorder)
|
|
||||||
m.symbol.set2dPattern(fpSize, m.size-fpSize-1, fpVBorder)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *regularSymbol) addAlignmentPatterns() {
|
|
||||||
for _, x := range alignmentPatternCenter[m.version.version] {
|
|
||||||
for _, y := range alignmentPatternCenter[m.version.version] {
|
|
||||||
if !m.symbol.empty(x, y) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
m.symbol.set2dPattern(x-2, y-2, alignmentPattern)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *regularSymbol) addTimingPatterns() {
|
|
||||||
value := true
|
|
||||||
|
|
||||||
for i := finderPatternSize + 1; i < m.size-finderPatternSize; i++ {
|
|
||||||
m.symbol.set(i, finderPatternSize-1, value)
|
|
||||||
m.symbol.set(finderPatternSize-1, i, value)
|
|
||||||
|
|
||||||
value = !value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *regularSymbol) addFormatInfo() {
|
|
||||||
fpSize := finderPatternSize
|
|
||||||
l := formatInfoLengthBits - 1
|
|
||||||
|
|
||||||
f := m.version.formatInfo(m.mask)
|
|
||||||
|
|
||||||
// Bits 0-7, under the top right finder pattern.
|
|
||||||
for i := 0; i <= 7; i++ {
|
|
||||||
m.symbol.set(m.size-i-1, fpSize+1, f.At(l-i))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bits 0-5, right of the top left finder pattern.
|
|
||||||
for i := 0; i <= 5; i++ {
|
|
||||||
m.symbol.set(fpSize+1, i, f.At(l-i))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bits 6-8 on the corner of the top left finder pattern.
|
|
||||||
m.symbol.set(fpSize+1, fpSize, f.At(l-6))
|
|
||||||
m.symbol.set(fpSize+1, fpSize+1, f.At(l-7))
|
|
||||||
m.symbol.set(fpSize, fpSize+1, f.At(l-8))
|
|
||||||
|
|
||||||
// Bits 9-14 on the underside of the top left finder pattern.
|
|
||||||
for i := 9; i <= 14; i++ {
|
|
||||||
m.symbol.set(14-i, fpSize+1, f.At(l-i))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bits 8-14 on the right side of the bottom left finder pattern.
|
|
||||||
for i := 8; i <= 14; i++ {
|
|
||||||
m.symbol.set(fpSize+1, m.size-fpSize+i-8, f.At(l-i))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always dark symbol.
|
|
||||||
m.symbol.set(fpSize+1, m.size-fpSize-1, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *regularSymbol) addVersionInfo() {
|
|
||||||
fpSize := finderPatternSize
|
|
||||||
|
|
||||||
v := m.version.versionInfo()
|
|
||||||
l := versionInfoLengthBits - 1
|
|
||||||
|
|
||||||
if v == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
// Above the bottom left finder pattern.
|
|
||||||
m.symbol.set(i/3, m.size-fpSize-4+i%3, v.At(l-i))
|
|
||||||
|
|
||||||
// Left of the top right finder pattern.
|
|
||||||
m.symbol.set(m.size-fpSize-4+i%3, i/3, v.At(l-i))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type direction uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
up direction = iota
|
|
||||||
down
|
|
||||||
)
|
|
||||||
|
|
||||||
func (m *regularSymbol) addData() (bool, error) {
|
|
||||||
xOffset := 1
|
|
||||||
dir := up
|
|
||||||
|
|
||||||
x := m.size - 2
|
|
||||||
y := m.size - 1
|
|
||||||
|
|
||||||
for i := 0; i < m.data.Len(); i++ {
|
|
||||||
var mask bool
|
|
||||||
switch m.mask {
|
|
||||||
case 0:
|
|
||||||
mask = (y+x+xOffset)%2 == 0
|
|
||||||
case 1:
|
|
||||||
mask = y%2 == 0
|
|
||||||
case 2:
|
|
||||||
mask = (x+xOffset)%3 == 0
|
|
||||||
case 3:
|
|
||||||
mask = (y+x+xOffset)%3 == 0
|
|
||||||
case 4:
|
|
||||||
mask = (y/2+(x+xOffset)/3)%2 == 0
|
|
||||||
case 5:
|
|
||||||
mask = (y*(x+xOffset))%2+(y*(x+xOffset))%3 == 0
|
|
||||||
case 6:
|
|
||||||
mask = ((y*(x+xOffset))%2+((y*(x+xOffset))%3))%2 == 0
|
|
||||||
case 7:
|
|
||||||
mask = ((y+x+xOffset)%2+((y*(x+xOffset))%3))%2 == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// != is equivalent to XOR.
|
|
||||||
m.symbol.set(x+xOffset, y, mask != m.data.At(i))
|
|
||||||
|
|
||||||
if i == m.data.Len()-1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find next free bit in the symbol.
|
|
||||||
for {
|
|
||||||
if xOffset == 1 {
|
|
||||||
xOffset = 0
|
|
||||||
} else {
|
|
||||||
xOffset = 1
|
|
||||||
|
|
||||||
if dir == up {
|
|
||||||
if y > 0 {
|
|
||||||
y--
|
|
||||||
} else {
|
|
||||||
dir = down
|
|
||||||
x -= 2
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if y < m.size-1 {
|
|
||||||
y++
|
|
||||||
} else {
|
|
||||||
dir = up
|
|
||||||
x -= 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip over the vertical timing pattern entirely.
|
|
||||||
if x == 5 {
|
|
||||||
x--
|
|
||||||
}
|
|
||||||
|
|
||||||
if m.symbol.empty(x+xOffset, y) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
309
vendor/github.com/skip2/go-qrcode/symbol.go
generated
vendored
309
vendor/github.com/skip2/go-qrcode/symbol.go
generated
vendored
@ -1,309 +0,0 @@
|
|||||||
// go-qrcode
|
|
||||||
// Copyright 2014 Tom Harwood
|
|
||||||
|
|
||||||
package qrcode
|
|
||||||
|
|
||||||
// symbol is a 2D array of bits representing a QR Code symbol.
|
|
||||||
//
|
|
||||||
// A symbol consists of size*size modules, with each module normally drawn as a
|
|
||||||
// black or white square. The symbol also has a border of quietZoneSize modules.
|
|
||||||
//
|
|
||||||
// A (fictional) size=2, quietZoneSize=1 QR Code looks like:
|
|
||||||
//
|
|
||||||
// +----+
|
|
||||||
// | |
|
|
||||||
// | ab |
|
|
||||||
// | cd |
|
|
||||||
// | |
|
|
||||||
// +----+
|
|
||||||
//
|
|
||||||
// For ease of implementation, the functions to set/get bits ignore the border,
|
|
||||||
// so (0,0)=a, (0,1)=b, (1,0)=c, and (1,1)=d. The entire symbol (including the
|
|
||||||
// border) is returned by bitmap().
|
|
||||||
//
|
|
||||||
type symbol struct {
|
|
||||||
// Value of module at [y][x]. True is set.
|
|
||||||
module [][]bool
|
|
||||||
|
|
||||||
// True if the module at [y][x] is used (to either true or false).
|
|
||||||
// Used to identify unused modules.
|
|
||||||
isUsed [][]bool
|
|
||||||
|
|
||||||
// Combined width/height of the symbol and quiet zones.
|
|
||||||
//
|
|
||||||
// size = symbolSize + 2*quietZoneSize.
|
|
||||||
size int
|
|
||||||
|
|
||||||
// Width/height of the symbol only.
|
|
||||||
symbolSize int
|
|
||||||
|
|
||||||
// Width/height of a single quiet zone.
|
|
||||||
quietZoneSize int
|
|
||||||
}
|
|
||||||
|
|
||||||
// newSymbol constructs a symbol of size size*size, with a border of
|
|
||||||
// quietZoneSize.
|
|
||||||
func newSymbol(size int, quietZoneSize int) *symbol {
|
|
||||||
var m symbol
|
|
||||||
|
|
||||||
m.module = make([][]bool, size+2*quietZoneSize)
|
|
||||||
m.isUsed = make([][]bool, size+2*quietZoneSize)
|
|
||||||
|
|
||||||
for i := range m.module {
|
|
||||||
m.module[i] = make([]bool, size+2*quietZoneSize)
|
|
||||||
m.isUsed[i] = make([]bool, size+2*quietZoneSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
m.size = size + 2*quietZoneSize
|
|
||||||
m.symbolSize = size
|
|
||||||
m.quietZoneSize = quietZoneSize
|
|
||||||
|
|
||||||
return &m
|
|
||||||
}
|
|
||||||
|
|
||||||
// get returns the module value at (x, y).
|
|
||||||
func (m *symbol) get(x int, y int) (v bool) {
|
|
||||||
v = m.module[y+m.quietZoneSize][x+m.quietZoneSize]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// empty returns true if the module at (x, y) has not been set (to either true
|
|
||||||
// or false).
|
|
||||||
func (m *symbol) empty(x int, y int) bool {
|
|
||||||
return !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize]
|
|
||||||
}
|
|
||||||
|
|
||||||
// numEmptyModules returns the number of empty modules.
|
|
||||||
//
|
|
||||||
// Initially numEmptyModules is symbolSize * symbolSize. After every module has
|
|
||||||
// been set (to either true or false), the number of empty modules is zero.
|
|
||||||
func (m *symbol) numEmptyModules() int {
|
|
||||||
var count int
|
|
||||||
for y := 0; y < m.symbolSize; y++ {
|
|
||||||
for x := 0; x < m.symbolSize; x++ {
|
|
||||||
if !m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
// set sets the module at (x, y) to v.
|
|
||||||
func (m *symbol) set(x int, y int, v bool) {
|
|
||||||
m.module[y+m.quietZoneSize][x+m.quietZoneSize] = v
|
|
||||||
m.isUsed[y+m.quietZoneSize][x+m.quietZoneSize] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// set2dPattern sets a 2D array of modules, starting at (x, y).
|
|
||||||
func (m *symbol) set2dPattern(x int, y int, v [][]bool) {
|
|
||||||
for j, row := range v {
|
|
||||||
for i, value := range row {
|
|
||||||
m.set(x+i, y+j, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bitmap returns the entire symbol, including the quiet zone.
|
|
||||||
func (m *symbol) bitmap() [][]bool {
|
|
||||||
module := make([][]bool, len(m.module))
|
|
||||||
|
|
||||||
for i := range m.module {
|
|
||||||
module[i] = m.module[i][:]
|
|
||||||
}
|
|
||||||
|
|
||||||
return module
|
|
||||||
}
|
|
||||||
|
|
||||||
// string returns a pictorial representation of the symbol, suitable for
|
|
||||||
// printing in a TTY.
|
|
||||||
func (m *symbol) string() string {
|
|
||||||
var result string
|
|
||||||
|
|
||||||
for _, row := range m.module {
|
|
||||||
for _, value := range row {
|
|
||||||
switch value {
|
|
||||||
case true:
|
|
||||||
result += " "
|
|
||||||
case false:
|
|
||||||
// Unicode 'FULL BLOCK' (U+2588).
|
|
||||||
result += "██"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result += "\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constants used to weight penalty calculations. Specified by ISO/IEC
|
|
||||||
// 18004:2006.
|
|
||||||
const (
|
|
||||||
penaltyWeight1 = 3
|
|
||||||
penaltyWeight2 = 3
|
|
||||||
penaltyWeight3 = 40
|
|
||||||
penaltyWeight4 = 10
|
|
||||||
)
|
|
||||||
|
|
||||||
// penaltyScore returns the penalty score of the symbol. The penalty score
|
|
||||||
// consists of the sum of the four individual penalty types.
|
|
||||||
func (m *symbol) penaltyScore() int {
|
|
||||||
return m.penalty1() + m.penalty2() + m.penalty3() + m.penalty4()
|
|
||||||
}
|
|
||||||
|
|
||||||
// penalty1 returns the penalty score for "adjacent modules in row/column with
|
|
||||||
// same colour".
|
|
||||||
//
|
|
||||||
// The numbers of adjacent matching modules and scores are:
|
|
||||||
// 0-5: score = 0
|
|
||||||
// 6+ : score = penaltyWeight1 + (numAdjacentModules - 5)
|
|
||||||
func (m *symbol) penalty1() int {
|
|
||||||
penalty := 0
|
|
||||||
|
|
||||||
for x := 0; x < m.symbolSize; x++ {
|
|
||||||
lastValue := m.get(x, 0)
|
|
||||||
count := 1
|
|
||||||
|
|
||||||
for y := 1; y < m.symbolSize; y++ {
|
|
||||||
v := m.get(x, y)
|
|
||||||
|
|
||||||
if v != lastValue {
|
|
||||||
count = 1
|
|
||||||
lastValue = v
|
|
||||||
} else {
|
|
||||||
count++
|
|
||||||
if count == 6 {
|
|
||||||
penalty += penaltyWeight1 + 1
|
|
||||||
} else if count > 6 {
|
|
||||||
penalty++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for y := 0; y < m.symbolSize; y++ {
|
|
||||||
lastValue := m.get(0, y)
|
|
||||||
count := 1
|
|
||||||
|
|
||||||
for x := 1; x < m.symbolSize; x++ {
|
|
||||||
v := m.get(x, y)
|
|
||||||
|
|
||||||
if v != lastValue {
|
|
||||||
count = 1
|
|
||||||
lastValue = v
|
|
||||||
} else {
|
|
||||||
count++
|
|
||||||
if count == 6 {
|
|
||||||
penalty += penaltyWeight1 + 1
|
|
||||||
} else if count > 6 {
|
|
||||||
penalty++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return penalty
|
|
||||||
}
|
|
||||||
|
|
||||||
// penalty2 returns the penalty score for "block of modules in the same colour".
|
|
||||||
//
|
|
||||||
// m*n: score = penaltyWeight2 * (m-1) * (n-1).
|
|
||||||
func (m *symbol) penalty2() int {
|
|
||||||
penalty := 0
|
|
||||||
|
|
||||||
for y := 1; y < m.symbolSize; y++ {
|
|
||||||
for x := 1; x < m.symbolSize; x++ {
|
|
||||||
topLeft := m.get(x-1, y-1)
|
|
||||||
above := m.get(x, y-1)
|
|
||||||
left := m.get(x-1, y)
|
|
||||||
current := m.get(x, y)
|
|
||||||
|
|
||||||
if current == left && current == above && current == topLeft {
|
|
||||||
penalty++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return penalty * penaltyWeight2
|
|
||||||
}
|
|
||||||
|
|
||||||
// penalty3 returns the penalty score for "1:1:3:1:1 ratio
|
|
||||||
// (dark:light:dark:light:dark) pattern in row/column, preceded or followed by
|
|
||||||
// light area 4 modules wide".
|
|
||||||
//
|
|
||||||
// Existence of the pattern scores penaltyWeight3.
|
|
||||||
func (m *symbol) penalty3() int {
|
|
||||||
penalty := 0
|
|
||||||
|
|
||||||
for y := 0; y < m.symbolSize; y++ {
|
|
||||||
var bitBuffer int16 = 0x00
|
|
||||||
|
|
||||||
for x := 0; x < m.symbolSize; x++ {
|
|
||||||
bitBuffer <<= 1
|
|
||||||
if v := m.get(x, y); v {
|
|
||||||
bitBuffer |= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
switch bitBuffer & 0x7ff {
|
|
||||||
// 0b000 0101 1101 or 0b10111010000
|
|
||||||
// 0x05d or 0x5d0
|
|
||||||
case 0x05d, 0x5d0:
|
|
||||||
penalty += penaltyWeight3
|
|
||||||
bitBuffer = 0xFF
|
|
||||||
default:
|
|
||||||
if x == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
|
|
||||||
penalty += penaltyWeight3
|
|
||||||
bitBuffer = 0xFF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for x := 0; x < m.symbolSize; x++ {
|
|
||||||
var bitBuffer int16 = 0x00
|
|
||||||
|
|
||||||
for y := 0; y < m.symbolSize; y++ {
|
|
||||||
bitBuffer <<= 1
|
|
||||||
if v := m.get(x, y); v {
|
|
||||||
bitBuffer |= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
switch bitBuffer & 0x7ff {
|
|
||||||
// 0b000 0101 1101 or 0b10111010000
|
|
||||||
// 0x05d or 0x5d0
|
|
||||||
case 0x05d, 0x5d0:
|
|
||||||
penalty += penaltyWeight3
|
|
||||||
bitBuffer = 0xFF
|
|
||||||
default:
|
|
||||||
if y == m.symbolSize-1 && (bitBuffer&0x7f) == 0x5d {
|
|
||||||
penalty += penaltyWeight3
|
|
||||||
bitBuffer = 0xFF
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return penalty
|
|
||||||
}
|
|
||||||
|
|
||||||
// penalty4 returns the penalty score...
|
|
||||||
func (m *symbol) penalty4() int {
|
|
||||||
numModules := m.symbolSize * m.symbolSize
|
|
||||||
numDarkModules := 0
|
|
||||||
|
|
||||||
for x := 0; x < m.symbolSize; x++ {
|
|
||||||
for y := 0; y < m.symbolSize; y++ {
|
|
||||||
if v := m.get(x, y); v {
|
|
||||||
numDarkModules++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
numDarkModuleDeviation := numModules/2 - numDarkModules
|
|
||||||
if numDarkModuleDeviation < 0 {
|
|
||||||
numDarkModuleDeviation *= -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return penaltyWeight4 * (numDarkModuleDeviation / (numModules / 20))
|
|
||||||
}
|
|
3050
vendor/github.com/skip2/go-qrcode/version.go
generated
vendored
3050
vendor/github.com/skip2/go-qrcode/version.go
generated
vendored
File diff suppressed because it is too large
Load Diff
674
vendor/go.mau.fi/libsignal/LICENSE
vendored
Normal file
674
vendor/go.mau.fi/libsignal/LICENSE
vendored
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
libsignal-protocol-go
|
||||||
|
Copyright (C) 2017 RadicalApp LLC
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
libsignal-protocol-go Copyright (C) 2017 RadicalApp LLC
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
101
vendor/go.mau.fi/libsignal/cipher/Cbc.go
vendored
Normal file
101
vendor/go.mau.fi/libsignal/cipher/Cbc.go
vendored
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
CBC describes a block cipher mode. In cryptography, a block cipher mode of operation is an algorithm that uses a
|
||||||
|
block cipher to provide an information service such as confidentiality or authenticity. A block cipher by itself
|
||||||
|
is only suitable for the secure cryptographic transformation (encryption or decryption) of one fixed-length group of
|
||||||
|
bits called a block. A mode of operation describes how to repeatedly apply a cipher's single-block operation to
|
||||||
|
securely transform amounts of data larger than a block.
|
||||||
|
|
||||||
|
This package simplifies the usage of AES-256-CBC.
|
||||||
|
*/
|
||||||
|
package cipher
|
||||||
|
|
||||||
|
/*
|
||||||
|
Some code is provided by the GitHub user locked (github.com/locked):
|
||||||
|
https://gist.github.com/locked/b066aa1ddeb2b28e855e
|
||||||
|
Thanks!
|
||||||
|
*/
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Decrypt is a function that decrypts a given cipher text with a provided key and initialization vector(iv).
|
||||||
|
*/
|
||||||
|
func DecryptCbc(iv, key, ciphertext []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ciphertext) < aes.BlockSize {
|
||||||
|
return nil, fmt.Errorf("ciphertext is shorter then block size: %d / %d", len(ciphertext), aes.BlockSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if iv == nil {
|
||||||
|
iv = ciphertext[:aes.BlockSize]
|
||||||
|
ciphertext = ciphertext[aes.BlockSize:]
|
||||||
|
}
|
||||||
|
|
||||||
|
cbc := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
cbc.CryptBlocks(ciphertext, ciphertext)
|
||||||
|
|
||||||
|
return unpad(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Encrypt is a function that encrypts plaintext with a given key and an optional initialization vector(iv).
|
||||||
|
*/
|
||||||
|
func EncryptCbc(iv, key, plaintext []byte) ([]byte, error) {
|
||||||
|
plaintext = pad(plaintext, aes.BlockSize)
|
||||||
|
|
||||||
|
if len(plaintext)%aes.BlockSize != 0 {
|
||||||
|
return nil, fmt.Errorf("plaintext is not a multiple of the block size: %d / %d", len(plaintext), aes.BlockSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ciphertext []byte
|
||||||
|
if iv == nil {
|
||||||
|
ciphertext = make([]byte, aes.BlockSize+len(plaintext))
|
||||||
|
iv := ciphertext[:aes.BlockSize]
|
||||||
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
cbc.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||||
|
} else {
|
||||||
|
ciphertext = make([]byte, len(plaintext))
|
||||||
|
|
||||||
|
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
cbc.CryptBlocks(ciphertext, plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func pad(ciphertext []byte, blockSize int) []byte {
|
||||||
|
padding := blockSize - len(ciphertext)%blockSize
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
return append(ciphertext, padtext...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unpad(src []byte) ([]byte, error) {
|
||||||
|
length := len(src)
|
||||||
|
padLen := int(src[length-1])
|
||||||
|
|
||||||
|
if padLen > length {
|
||||||
|
return nil, fmt.Errorf("padding is greater then the length: %d / %d", padLen, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
return src[:(length - padLen)], nil
|
||||||
|
}
|
105
vendor/go.mau.fi/libsignal/cipher/Cipher.go
vendored
Normal file
105
vendor/go.mau.fi/libsignal/cipher/Cipher.go
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Package cipher is a package for common encrypt/decrypt of symmetric key messages.
|
||||||
|
package cipher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decrypt will use the given key, iv, and ciphertext and return
|
||||||
|
// the plaintext bytes.
|
||||||
|
func Decrypt(iv, key, ciphertext []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(ciphertext) < aes.BlockSize {
|
||||||
|
return nil, errors.New("ciphertext too short")
|
||||||
|
}
|
||||||
|
cbc := cipher.NewCBCDecrypter(block, iv)
|
||||||
|
cbc.CryptBlocks(ciphertext, ciphertext)
|
||||||
|
|
||||||
|
unpaddedText, err := pkcs7Unpad(ciphertext, aes.BlockSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return unpaddedText, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt will use the given iv, key, and plaintext bytes
|
||||||
|
// and return ciphertext bytes.
|
||||||
|
func Encrypt(iv, key, plaintext []byte) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
paddedText, err := pkcs7Pad(plaintext, block.BlockSize())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ciphertext := make([]byte, len(paddedText))
|
||||||
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
|
mode.CryptBlocks(ciphertext, paddedText)
|
||||||
|
|
||||||
|
return ciphertext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PKCS7 padding.
|
||||||
|
|
||||||
|
// PKCS7 errors.
|
||||||
|
var (
|
||||||
|
// ErrInvalidBlockSize indicates hash blocksize <= 0.
|
||||||
|
ErrInvalidBlockSize = errors.New("invalid blocksize")
|
||||||
|
|
||||||
|
// ErrInvalidPKCS7Data indicates bad input to PKCS7 pad or unpad.
|
||||||
|
ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data (empty or not padded)")
|
||||||
|
|
||||||
|
// ErrInvalidPKCS7Padding indicates PKCS7 unpad fails to bad input.
|
||||||
|
ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
|
||||||
|
)
|
||||||
|
|
||||||
|
// pkcs7Pad right-pads the given byte slice with 1 to n bytes, where
|
||||||
|
// n is the block size. The size of the result is x times n, where x
|
||||||
|
// is at least 1.
|
||||||
|
func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
|
||||||
|
if blocksize <= 0 {
|
||||||
|
return nil, ErrInvalidBlockSize
|
||||||
|
}
|
||||||
|
if b == nil || len(b) == 0 {
|
||||||
|
return nil, ErrInvalidPKCS7Data
|
||||||
|
}
|
||||||
|
n := blocksize - (len(b) % blocksize)
|
||||||
|
pb := make([]byte, len(b)+n)
|
||||||
|
copy(pb, b)
|
||||||
|
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
|
||||||
|
return pb, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkcs7Unpad validates and unpads data from the given bytes slice.
|
||||||
|
// The returned value will be 1 to n bytes smaller depending on the
|
||||||
|
// amount of padding, where n is the block size.
|
||||||
|
func pkcs7Unpad(b []byte, blocksize int) ([]byte, error) {
|
||||||
|
if blocksize <= 0 {
|
||||||
|
return nil, ErrInvalidBlockSize
|
||||||
|
}
|
||||||
|
if b == nil || len(b) == 0 {
|
||||||
|
return nil, ErrInvalidPKCS7Data
|
||||||
|
}
|
||||||
|
if len(b)%blocksize != 0 {
|
||||||
|
return nil, ErrInvalidPKCS7Padding
|
||||||
|
}
|
||||||
|
c := b[len(b)-1]
|
||||||
|
n := int(c)
|
||||||
|
if n == 0 || n > len(b) {
|
||||||
|
return nil, ErrInvalidPKCS7Padding
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if b[len(b)-n+i] != c {
|
||||||
|
return nil, ErrInvalidPKCS7Padding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b[:len(b)-n], nil
|
||||||
|
}
|
109
vendor/go.mau.fi/libsignal/ecc/Curve.go
vendored
Normal file
109
vendor/go.mau.fi/libsignal/ecc/Curve.go
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package ecc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/curve25519"
|
||||||
|
|
||||||
|
"go.mau.fi/libsignal/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DjbType is the Diffie-Hellman curve type (curve25519) created by D. J. Bernstein.
|
||||||
|
const DjbType = 0x05
|
||||||
|
|
||||||
|
var ErrBadKeyType = errors.New("bad key type")
|
||||||
|
|
||||||
|
// DecodePoint will take the given bytes and offset and return an ECPublicKeyable object.
|
||||||
|
// This is used to check the byte at the given offset in the byte array for a special
|
||||||
|
// "type" byte that will determine the key type. Currently only DJB EC keys are supported.
|
||||||
|
func DecodePoint(bytes []byte, offset int) (ECPublicKeyable, error) {
|
||||||
|
keyType := bytes[offset] & 0xFF
|
||||||
|
|
||||||
|
switch keyType {
|
||||||
|
case DjbType:
|
||||||
|
keyBytes := [32]byte{}
|
||||||
|
copy(keyBytes[:], bytes[offset+1:])
|
||||||
|
return NewDjbECPublicKey(keyBytes), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("%w %d", ErrBadKeyType, keyType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateKeyPair(privateKey []byte) *ECKeyPair {
|
||||||
|
var private, public [32]byte
|
||||||
|
copy(private[:], privateKey)
|
||||||
|
|
||||||
|
private[0] &= 248
|
||||||
|
private[31] &= 127
|
||||||
|
private[31] |= 64
|
||||||
|
|
||||||
|
curve25519.ScalarBaseMult(&public, &private)
|
||||||
|
|
||||||
|
// Put data into our keypair struct
|
||||||
|
djbECPub := NewDjbECPublicKey(public)
|
||||||
|
djbECPriv := NewDjbECPrivateKey(private)
|
||||||
|
keypair := NewECKeyPair(djbECPub, djbECPriv)
|
||||||
|
|
||||||
|
logger.Debug("Returning keypair: ", keypair)
|
||||||
|
return keypair
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKeyPair returns an EC Key Pair.
|
||||||
|
func GenerateKeyPair() (*ECKeyPair, error) {
|
||||||
|
// logger.Debug("Generating EC Key Pair...")
|
||||||
|
// Get cryptographically secure random numbers.
|
||||||
|
random := rand.Reader
|
||||||
|
|
||||||
|
// Create a byte array for our public and private keys.
|
||||||
|
var private, public [32]byte
|
||||||
|
|
||||||
|
// Generate some random data
|
||||||
|
_, err := io.ReadFull(random, private[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Documented at: http://cr.yp.to/ecdh.html
|
||||||
|
private[0] &= 248
|
||||||
|
private[31] &= 127
|
||||||
|
private[31] |= 64
|
||||||
|
|
||||||
|
curve25519.ScalarBaseMult(&public, &private)
|
||||||
|
|
||||||
|
// Put data into our keypair struct
|
||||||
|
djbECPub := NewDjbECPublicKey(public)
|
||||||
|
djbECPriv := NewDjbECPrivateKey(private)
|
||||||
|
keypair := NewECKeyPair(djbECPub, djbECPriv)
|
||||||
|
|
||||||
|
// logger.Debug("Returning keypair: ", keypair)
|
||||||
|
|
||||||
|
return keypair, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifySignature verifies that the message was signed with the given key.
|
||||||
|
func VerifySignature(signingKey ECPublicKeyable, message []byte, signature [64]byte) bool {
|
||||||
|
logger.Debug("Verifying signature of bytes: ", message)
|
||||||
|
publicKey := signingKey.PublicKey()
|
||||||
|
valid := verify(publicKey, message, &signature)
|
||||||
|
logger.Debug("Signature valid: ", valid)
|
||||||
|
return valid
|
||||||
|
}
|
||||||
|
|
||||||
|
// CalculateSignature signs a message with the given private key.
|
||||||
|
func CalculateSignature(signingKey ECPrivateKeyable, message []byte) [64]byte {
|
||||||
|
logger.Debug("Signing bytes with signing key")
|
||||||
|
// Get cryptographically secure random numbers.
|
||||||
|
var random [64]byte
|
||||||
|
r := rand.Reader
|
||||||
|
io.ReadFull(r, random[:])
|
||||||
|
|
||||||
|
// Get the private key.
|
||||||
|
privateKey := signingKey.Serialize()
|
||||||
|
|
||||||
|
// Sign the message.
|
||||||
|
signature := sign(&privateKey, message, random)
|
||||||
|
return *signature
|
||||||
|
}
|
29
vendor/go.mau.fi/libsignal/ecc/DjbECPublicKey.go
vendored
Normal file
29
vendor/go.mau.fi/libsignal/ecc/DjbECPublicKey.go
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ecc
|
||||||
|
|
||||||
|
// NewDjbECPublicKey creates a new Curve25519 public key with the given bytes.
|
||||||
|
func NewDjbECPublicKey(publicKey [32]byte) *DjbECPublicKey {
|
||||||
|
key := DjbECPublicKey{
|
||||||
|
publicKey: publicKey,
|
||||||
|
}
|
||||||
|
return &key
|
||||||
|
}
|
||||||
|
|
||||||
|
// DjbECPublicKey implements the ECPublicKey interface and uses Curve25519.
|
||||||
|
type DjbECPublicKey struct {
|
||||||
|
publicKey [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey returns the EC public key as a byte array.
|
||||||
|
func (d *DjbECPublicKey) PublicKey() [32]byte {
|
||||||
|
return d.publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize returns the public key prepended by the DjbType value.
|
||||||
|
func (d *DjbECPublicKey) Serialize() []byte {
|
||||||
|
return append([]byte{DjbType}, d.publicKey[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the DjbType value.
|
||||||
|
func (d *DjbECPublicKey) Type() int {
|
||||||
|
return DjbType
|
||||||
|
}
|
29
vendor/go.mau.fi/libsignal/ecc/DkbECPrivateKey.go
vendored
Normal file
29
vendor/go.mau.fi/libsignal/ecc/DkbECPrivateKey.go
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package ecc
|
||||||
|
|
||||||
|
// NewDjbECPrivateKey returns a new EC private key with the given bytes.
|
||||||
|
func NewDjbECPrivateKey(key [32]byte) *DjbECPrivateKey {
|
||||||
|
private := DjbECPrivateKey{
|
||||||
|
privateKey: key,
|
||||||
|
}
|
||||||
|
return &private
|
||||||
|
}
|
||||||
|
|
||||||
|
// DjbECPrivateKey implements the ECPrivateKey interface and uses Curve25519.
|
||||||
|
type DjbECPrivateKey struct {
|
||||||
|
privateKey [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKey returns the private key as a byte-array.
|
||||||
|
func (d *DjbECPrivateKey) PrivateKey() [32]byte {
|
||||||
|
return d.privateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize returns the private key as a byte-array.
|
||||||
|
func (d *DjbECPrivateKey) Serialize() [32]byte {
|
||||||
|
return d.privateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the EC type value.
|
||||||
|
func (d *DjbECPrivateKey) Type() int {
|
||||||
|
return DjbType
|
||||||
|
}
|
3
vendor/go.mau.fi/libsignal/ecc/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/ecc/Doc.go
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package ecc provides a way to generate, sign, and use Elliptic-Curve
|
||||||
|
// X25519 Cryptography keys.
|
||||||
|
package ecc
|
27
vendor/go.mau.fi/libsignal/ecc/ECKeyPair.go
vendored
Normal file
27
vendor/go.mau.fi/libsignal/ecc/ECKeyPair.go
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package ecc
|
||||||
|
|
||||||
|
// NewECKeyPair returns a new elliptic curve keypair given the specified public and private keys.
|
||||||
|
func NewECKeyPair(publicKey ECPublicKeyable, privateKey ECPrivateKeyable) *ECKeyPair {
|
||||||
|
keypair := ECKeyPair{
|
||||||
|
publicKey: publicKey,
|
||||||
|
privateKey: privateKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &keypair
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECKeyPair is a combination of both public and private elliptic curve keys.
|
||||||
|
type ECKeyPair struct {
|
||||||
|
publicKey ECPublicKeyable
|
||||||
|
privateKey ECPrivateKeyable
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKey returns the public key from the key pair.
|
||||||
|
func (e *ECKeyPair) PublicKey() ECPublicKeyable {
|
||||||
|
return e.publicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivateKey returns the private key from the key pair.
|
||||||
|
func (e *ECKeyPair) PrivateKey() ECPrivateKeyable {
|
||||||
|
return e.privateKey
|
||||||
|
}
|
7
vendor/go.mau.fi/libsignal/ecc/ECPrivateKey.go
vendored
Normal file
7
vendor/go.mau.fi/libsignal/ecc/ECPrivateKey.go
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package ecc
|
||||||
|
|
||||||
|
// ECPrivateKeyable is an interface for all elliptic curve private keys.
|
||||||
|
type ECPrivateKeyable interface {
|
||||||
|
Serialize() [32]byte
|
||||||
|
Type() int
|
||||||
|
}
|
11
vendor/go.mau.fi/libsignal/ecc/ECPublicKey.go
vendored
Normal file
11
vendor/go.mau.fi/libsignal/ecc/ECPublicKey.go
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package ecc
|
||||||
|
|
||||||
|
// KeySize is the size of EC keys (32) with the EC type byte prepended to it.
|
||||||
|
const KeySize int = 33
|
||||||
|
|
||||||
|
// ECPublicKeyable is an interface for all elliptic curve public keys.
|
||||||
|
type ECPublicKeyable interface {
|
||||||
|
Serialize() []byte
|
||||||
|
Type() int
|
||||||
|
PublicKey() [32]byte
|
||||||
|
}
|
97
vendor/go.mau.fi/libsignal/ecc/SignCurve25519.go
vendored
Normal file
97
vendor/go.mau.fi/libsignal/ecc/SignCurve25519.go
vendored
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package ecc
|
||||||
|
|
||||||
|
// Package curve25519sign implements a signature scheme based on Curve25519 keys.
|
||||||
|
// See https://moderncrypto.org/mail-archive/curves/2014/000205.html for details.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/sha512"
|
||||||
|
|
||||||
|
"filippo.io/edwards25519"
|
||||||
|
"filippo.io/edwards25519/field"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sign signs the message with privateKey and returns a signature as a byte slice.
|
||||||
|
func sign(privateKey *[32]byte, message []byte, random [64]byte) *[64]byte {
|
||||||
|
|
||||||
|
// Calculate Ed25519 public key from Curve25519 private key
|
||||||
|
var A edwards25519.Point
|
||||||
|
privateKeyScalar, _ := edwards25519.NewScalar().SetBytesWithClamping(privateKey[:])
|
||||||
|
A.ScalarBaseMult(privateKeyScalar)
|
||||||
|
publicKey := *(*[32]byte)(A.Bytes())
|
||||||
|
|
||||||
|
// Calculate r
|
||||||
|
diversifier := [32]byte{
|
||||||
|
0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
|
||||||
|
|
||||||
|
var r [64]byte
|
||||||
|
hash := sha512.New()
|
||||||
|
hash.Write(diversifier[:])
|
||||||
|
hash.Write(privateKey[:])
|
||||||
|
hash.Write(message)
|
||||||
|
hash.Write(random[:])
|
||||||
|
hash.Sum(r[:0])
|
||||||
|
|
||||||
|
// Calculate R
|
||||||
|
var rReduced *edwards25519.Scalar
|
||||||
|
rReduced, _ = edwards25519.NewScalar().SetUniformBytes(r[:])
|
||||||
|
var R edwards25519.Point
|
||||||
|
R.ScalarBaseMult(rReduced)
|
||||||
|
|
||||||
|
var encodedR [32]byte
|
||||||
|
encodedR = *(*[32]byte)(R.Bytes())
|
||||||
|
|
||||||
|
// Calculate S = r + SHA2-512(R || A_ed || msg) * a (mod L)
|
||||||
|
var hramDigest [64]byte
|
||||||
|
hash.Reset()
|
||||||
|
hash.Write(encodedR[:])
|
||||||
|
hash.Write(publicKey[:])
|
||||||
|
hash.Write(message)
|
||||||
|
hash.Sum(hramDigest[:0])
|
||||||
|
hramDigestReduced, _ := edwards25519.NewScalar().SetUniformBytes(hramDigest[:])
|
||||||
|
|
||||||
|
sScalar := edwards25519.NewScalar().MultiplyAdd(hramDigestReduced, privateKeyScalar, rReduced)
|
||||||
|
s := *(*[32]byte)(sScalar.Bytes())
|
||||||
|
|
||||||
|
signature := new([64]byte)
|
||||||
|
copy(signature[:], encodedR[:])
|
||||||
|
copy(signature[32:], s[:])
|
||||||
|
signature[63] |= publicKey[31] & 0x80
|
||||||
|
|
||||||
|
return signature
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify checks whether the message has a valid signature.
|
||||||
|
func verify(publicKey [32]byte, message []byte, signature *[64]byte) bool {
|
||||||
|
|
||||||
|
publicKey[31] &= 0x7F
|
||||||
|
|
||||||
|
/* Convert the Curve25519 public key into an Ed25519 public key. In
|
||||||
|
particular, convert Curve25519's "montgomery" x-coordinate into an
|
||||||
|
Ed25519 "edwards" y-coordinate:
|
||||||
|
|
||||||
|
ed_y = (mont_x - 1) / (mont_x + 1)
|
||||||
|
|
||||||
|
NOTE: mont_x=-1 is converted to ed_y=0 since fe_invert is mod-exp
|
||||||
|
|
||||||
|
Then move the sign bit into the pubkey from the signature.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var edY, one, montX, montXMinusOne, montXPlusOne field.Element
|
||||||
|
_, _ = montX.SetBytes(publicKey[:])
|
||||||
|
_ = one.One()
|
||||||
|
montXMinusOne.Subtract(&montX, &one)
|
||||||
|
montXPlusOne.Add(&montX, &one)
|
||||||
|
montXPlusOne.Invert(&montXPlusOne)
|
||||||
|
edY.Multiply(&montXMinusOne, &montXPlusOne)
|
||||||
|
|
||||||
|
A_ed := *(*[32]byte)(edY.Bytes())
|
||||||
|
|
||||||
|
A_ed[31] |= signature[63] & 0x80
|
||||||
|
signature[63] &= 0x7F
|
||||||
|
|
||||||
|
return ed25519.Verify(A_ed[:], message, signature[:])
|
||||||
|
}
|
141
vendor/go.mau.fi/libsignal/groups/GroupCipher.go
vendored
Normal file
141
vendor/go.mau.fi/libsignal/groups/GroupCipher.go
vendored
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
package groups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.mau.fi/libsignal/cipher"
|
||||||
|
"go.mau.fi/libsignal/ecc"
|
||||||
|
"go.mau.fi/libsignal/groups/ratchet"
|
||||||
|
"go.mau.fi/libsignal/groups/state/record"
|
||||||
|
"go.mau.fi/libsignal/groups/state/store"
|
||||||
|
"go.mau.fi/libsignal/protocol"
|
||||||
|
"go.mau.fi/libsignal/signalerror"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewGroupCipher will return a new group message cipher that can be used for
|
||||||
|
// encrypt/decrypt operations.
|
||||||
|
func NewGroupCipher(builder *SessionBuilder, senderKeyID *protocol.SenderKeyName,
|
||||||
|
senderKeyStore store.SenderKey) *GroupCipher {
|
||||||
|
|
||||||
|
return &GroupCipher{
|
||||||
|
senderKeyID: senderKeyID,
|
||||||
|
senderKeyStore: senderKeyStore,
|
||||||
|
sessionBuilder: builder,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupCipher is the main entry point for group encrypt/decrypt operations.
|
||||||
|
// Once a session has been established, this can be used for
|
||||||
|
// all encrypt/decrypt operations within that session.
|
||||||
|
type GroupCipher struct {
|
||||||
|
senderKeyID *protocol.SenderKeyName
|
||||||
|
senderKeyStore store.SenderKey
|
||||||
|
sessionBuilder *SessionBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt will take the given message in bytes and return encrypted bytes.
|
||||||
|
func (c *GroupCipher) Encrypt(plaintext []byte) (protocol.GroupCiphertextMessage, error) {
|
||||||
|
// Load the sender key based on id from our store.
|
||||||
|
keyRecord := c.senderKeyStore.LoadSenderKey(c.senderKeyID)
|
||||||
|
senderKeyState, err := keyRecord.SenderKeyState()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the message key from the senderkey state.
|
||||||
|
senderKey, err := senderKeyState.SenderChainKey().SenderMessageKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt the plaintext.
|
||||||
|
ciphertext, err := cipher.EncryptCbc(senderKey.Iv(), senderKey.CipherKey(), plaintext)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
senderKeyMessage := protocol.NewSenderKeyMessage(
|
||||||
|
senderKeyState.KeyID(),
|
||||||
|
senderKey.Iteration(),
|
||||||
|
ciphertext,
|
||||||
|
senderKeyState.SigningKey().PrivateKey(),
|
||||||
|
c.sessionBuilder.serializer.SenderKeyMessage,
|
||||||
|
)
|
||||||
|
|
||||||
|
senderKeyState.SetSenderChainKey(senderKeyState.SenderChainKey().Next())
|
||||||
|
c.senderKeyStore.StoreSenderKey(c.senderKeyID, keyRecord)
|
||||||
|
|
||||||
|
return senderKeyMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt decrypts the given message using an existing session that
|
||||||
|
// is stored in the senderKey store.
|
||||||
|
func (c *GroupCipher) Decrypt(senderKeyMessage *protocol.SenderKeyMessage) ([]byte, error) {
|
||||||
|
keyRecord := c.senderKeyStore.LoadSenderKey(c.senderKeyID)
|
||||||
|
|
||||||
|
if keyRecord.IsEmpty() {
|
||||||
|
return nil, fmt.Errorf("%w for %s in %s", signalerror.ErrNoSenderKeyForUser, c.senderKeyID.Sender().String(), c.senderKeyID.GroupID())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the senderkey state by id.
|
||||||
|
senderKeyState, err := keyRecord.GetSenderKeyStateByID(senderKeyMessage.KeyID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the signature of the senderkey message.
|
||||||
|
verified := c.verifySignature(senderKeyState.SigningKey().PublicKey(), senderKeyMessage)
|
||||||
|
if !verified {
|
||||||
|
return nil, signalerror.ErrSenderKeyStateVerificationFailed
|
||||||
|
}
|
||||||
|
|
||||||
|
senderKey, err := c.getSenderKey(senderKeyState, senderKeyMessage.Iteration())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt the message ciphertext.
|
||||||
|
plaintext, err := cipher.DecryptCbc(senderKey.Iv(), senderKey.CipherKey(), senderKeyMessage.Ciphertext())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the sender key by id.
|
||||||
|
c.senderKeyStore.StoreSenderKey(c.senderKeyID, keyRecord)
|
||||||
|
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifySignature will verify the signature of the senderkey message with
|
||||||
|
// the given public key.
|
||||||
|
func (c *GroupCipher) verifySignature(signingPubKey ecc.ECPublicKeyable,
|
||||||
|
senderKeyMessage *protocol.SenderKeyMessage) bool {
|
||||||
|
|
||||||
|
return ecc.VerifySignature(signingPubKey, senderKeyMessage.Serialize(), senderKeyMessage.Signature())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *GroupCipher) getSenderKey(senderKeyState *record.SenderKeyState, iteration uint32) (*ratchet.SenderMessageKey, error) {
|
||||||
|
senderChainKey := senderKeyState.SenderChainKey()
|
||||||
|
if senderChainKey.Iteration() > iteration {
|
||||||
|
if senderKeyState.HasSenderMessageKey(iteration) {
|
||||||
|
return senderKeyState.RemoveSenderMessageKey(iteration), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%w (current: %d, received: %d)", signalerror.ErrOldCounter, senderChainKey.Iteration(), iteration)
|
||||||
|
}
|
||||||
|
|
||||||
|
if iteration-senderChainKey.Iteration() > 2000 {
|
||||||
|
return nil, signalerror.ErrTooFarIntoFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
for senderChainKey.Iteration() < iteration {
|
||||||
|
senderMessageKey, err := senderChainKey.SenderMessageKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
senderKeyState.AddSenderMessageKey(senderMessageKey)
|
||||||
|
senderChainKey = senderChainKey.Next()
|
||||||
|
}
|
||||||
|
|
||||||
|
senderKeyState.SetSenderChainKey(senderChainKey.Next())
|
||||||
|
return senderChainKey.SenderMessageKey()
|
||||||
|
}
|
84
vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go
vendored
Normal file
84
vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
// Package groups is responsible for setting up group SenderKey encrypted sessions.
|
||||||
|
// Once a session has been established, GroupCipher can be used to encrypt/decrypt
|
||||||
|
// messages in that session.
|
||||||
|
//
|
||||||
|
// The built sessions are unidirectional: they can be used either for sending or
|
||||||
|
// for receiving, but not both. Sessions are constructed per (groupId + senderId +
|
||||||
|
// deviceId) tuple. Remote logical users are identified by their senderId, and each
|
||||||
|
// logical recipientId can have multiple physical devices.
|
||||||
|
package groups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.mau.fi/libsignal/groups/state/record"
|
||||||
|
"go.mau.fi/libsignal/groups/state/store"
|
||||||
|
"go.mau.fi/libsignal/protocol"
|
||||||
|
"go.mau.fi/libsignal/serialize"
|
||||||
|
"go.mau.fi/libsignal/util/keyhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewGroupSessionBuilder will return a new group session builder.
|
||||||
|
func NewGroupSessionBuilder(senderKeyStore store.SenderKey,
|
||||||
|
serializer *serialize.Serializer) *SessionBuilder {
|
||||||
|
|
||||||
|
return &SessionBuilder{
|
||||||
|
senderKeyStore: senderKeyStore,
|
||||||
|
serializer: serializer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SessionBuilder is a structure for building group sessions.
|
||||||
|
type SessionBuilder struct {
|
||||||
|
senderKeyStore store.SenderKey
|
||||||
|
serializer *serialize.Serializer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process will process an incoming group message and set up the corresponding
|
||||||
|
// session for it.
|
||||||
|
func (b *SessionBuilder) Process(senderKeyName *protocol.SenderKeyName,
|
||||||
|
msg *protocol.SenderKeyDistributionMessage) {
|
||||||
|
|
||||||
|
senderKeyRecord := b.senderKeyStore.LoadSenderKey(senderKeyName)
|
||||||
|
if senderKeyRecord == nil {
|
||||||
|
senderKeyRecord = record.NewSenderKey(b.serializer.SenderKeyRecord, b.serializer.SenderKeyState)
|
||||||
|
}
|
||||||
|
senderKeyRecord.AddSenderKeyState(msg.ID(), msg.Iteration(), msg.ChainKey(), msg.SignatureKey())
|
||||||
|
b.senderKeyStore.StoreSenderKey(senderKeyName, senderKeyRecord)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new group session for the given name.
|
||||||
|
func (b *SessionBuilder) Create(senderKeyName *protocol.SenderKeyName) (*protocol.SenderKeyDistributionMessage, error) {
|
||||||
|
// Load the senderkey by name
|
||||||
|
senderKeyRecord := b.senderKeyStore.LoadSenderKey(senderKeyName)
|
||||||
|
|
||||||
|
// If the record is empty, generate new keys.
|
||||||
|
if senderKeyRecord == nil || senderKeyRecord.IsEmpty() {
|
||||||
|
senderKeyRecord = record.NewSenderKey(b.serializer.SenderKeyRecord, b.serializer.SenderKeyState)
|
||||||
|
signingKey, err := keyhelper.GenerateSenderSigningKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
senderKeyRecord.SetSenderKeyState(
|
||||||
|
keyhelper.GenerateSenderKeyID(), 0,
|
||||||
|
keyhelper.GenerateSenderKey(),
|
||||||
|
signingKey,
|
||||||
|
)
|
||||||
|
b.senderKeyStore.StoreSenderKey(senderKeyName, senderKeyRecord)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the senderkey state.
|
||||||
|
state, err := senderKeyRecord.SenderKeyState()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the group message to return.
|
||||||
|
senderKeyDistributionMessage := protocol.NewSenderKeyDistributionMessage(
|
||||||
|
state.KeyID(),
|
||||||
|
state.SenderChainKey().Iteration(),
|
||||||
|
state.SenderChainKey().Seed(),
|
||||||
|
state.SigningKey().PublicKey(),
|
||||||
|
b.serializer.SenderKeyDistributionMessage,
|
||||||
|
)
|
||||||
|
|
||||||
|
return senderKeyDistributionMessage, nil
|
||||||
|
}
|
3
vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package ratchet provides the methods necessary to establish a ratchet
|
||||||
|
// session for group messaging.
|
||||||
|
package ratchet
|
68
vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go
vendored
Normal file
68
vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package ratchet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
)
|
||||||
|
|
||||||
|
var messageKeySeed = []byte{0x01}
|
||||||
|
var chainKeySeed = []byte{0x02}
|
||||||
|
|
||||||
|
// NewSenderChainKey will return a new SenderChainKey.
|
||||||
|
func NewSenderChainKey(iteration uint32, chainKey []byte) *SenderChainKey {
|
||||||
|
return &SenderChainKey{
|
||||||
|
iteration: iteration,
|
||||||
|
chainKey: chainKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderChainKeyFromStruct will return a new chain key object from the
|
||||||
|
// given serializeable structure.
|
||||||
|
func NewSenderChainKeyFromStruct(structure *SenderChainKeyStructure) *SenderChainKey {
|
||||||
|
return &SenderChainKey{
|
||||||
|
iteration: structure.Iteration,
|
||||||
|
chainKey: structure.ChainKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStructFromSenderChainKeys returns a serializeable structure of chain keys.
|
||||||
|
func NewStructFromSenderChainKey(key *SenderChainKey) *SenderChainKeyStructure {
|
||||||
|
return &SenderChainKeyStructure{
|
||||||
|
Iteration: key.iteration,
|
||||||
|
ChainKey: key.chainKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderChainKeyStructure is a serializeable structure of SenderChainKeys.
|
||||||
|
type SenderChainKeyStructure struct {
|
||||||
|
Iteration uint32
|
||||||
|
ChainKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type SenderChainKey struct {
|
||||||
|
iteration uint32
|
||||||
|
chainKey []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *SenderChainKey) Iteration() uint32 {
|
||||||
|
return k.iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *SenderChainKey) SenderMessageKey() (*SenderMessageKey, error) {
|
||||||
|
return NewSenderMessageKey(k.iteration, k.getDerivative(messageKeySeed, k.chainKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *SenderChainKey) Next() *SenderChainKey {
|
||||||
|
return NewSenderChainKey(k.iteration+1, k.getDerivative(chainKeySeed, k.chainKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *SenderChainKey) Seed() []byte {
|
||||||
|
return k.chainKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *SenderChainKey) getDerivative(seed []byte, key []byte) []byte {
|
||||||
|
mac := hmac.New(sha256.New, key[:])
|
||||||
|
mac.Write(seed)
|
||||||
|
|
||||||
|
return mac.Sum(nil)
|
||||||
|
}
|
89
vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go
vendored
Normal file
89
vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package ratchet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.mau.fi/libsignal/kdf"
|
||||||
|
"go.mau.fi/libsignal/util/bytehelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KdfInfo is optional bytes to include in deriving secrets with KDF.
|
||||||
|
const KdfInfo string = "WhisperGroup"
|
||||||
|
|
||||||
|
// NewSenderMessageKey will return a new sender message key using the given
|
||||||
|
// iteration and seed.
|
||||||
|
func NewSenderMessageKey(iteration uint32, seed []byte) (*SenderMessageKey, error) {
|
||||||
|
derivative, err := kdf.DeriveSecrets(seed, nil, []byte(KdfInfo), 48)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split our derived secrets into 2 parts
|
||||||
|
parts := bytehelper.Split(derivative, 16, 32)
|
||||||
|
|
||||||
|
// Build the message key.
|
||||||
|
senderKeyMessage := &SenderMessageKey{
|
||||||
|
iteration: iteration,
|
||||||
|
seed: seed,
|
||||||
|
iv: parts[0],
|
||||||
|
cipherKey: parts[1],
|
||||||
|
}
|
||||||
|
|
||||||
|
return senderKeyMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderMessageKeyFromStruct will return a new message key object from the
|
||||||
|
// given serializeable structure.
|
||||||
|
func NewSenderMessageKeyFromStruct(structure *SenderMessageKeyStructure) *SenderMessageKey {
|
||||||
|
return &SenderMessageKey{
|
||||||
|
iteration: structure.Iteration,
|
||||||
|
iv: structure.IV,
|
||||||
|
cipherKey: structure.CipherKey,
|
||||||
|
seed: structure.Seed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStructFromSenderMessageKey returns a serializeable structure of message keys.
|
||||||
|
func NewStructFromSenderMessageKey(key *SenderMessageKey) *SenderMessageKeyStructure {
|
||||||
|
return &SenderMessageKeyStructure{
|
||||||
|
CipherKey: key.cipherKey,
|
||||||
|
Iteration: key.iteration,
|
||||||
|
IV: key.iv,
|
||||||
|
Seed: key.seed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderMessageKeyStructure is a serializeable structure of SenderMessageKeys.
|
||||||
|
type SenderMessageKeyStructure struct {
|
||||||
|
Iteration uint32
|
||||||
|
IV []byte
|
||||||
|
CipherKey []byte
|
||||||
|
Seed []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderMessageKey is a structure for sender message keys used in group
|
||||||
|
// messaging.
|
||||||
|
type SenderMessageKey struct {
|
||||||
|
iteration uint32
|
||||||
|
iv []byte
|
||||||
|
cipherKey []byte
|
||||||
|
seed []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iteration will return the sender message key's iteration.
|
||||||
|
func (k *SenderMessageKey) Iteration() uint32 {
|
||||||
|
return k.iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iv will return the sender message key's initialization vector.
|
||||||
|
func (k *SenderMessageKey) Iv() []byte {
|
||||||
|
return k.iv
|
||||||
|
}
|
||||||
|
|
||||||
|
// CipherKey will return the key in bytes.
|
||||||
|
func (k *SenderMessageKey) CipherKey() []byte {
|
||||||
|
return k.cipherKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seed will return the sender message key's seed.
|
||||||
|
func (k *SenderMessageKey) Seed() []byte {
|
||||||
|
return k.seed
|
||||||
|
}
|
2
vendor/go.mau.fi/libsignal/groups/state/record/Doc.go
vendored
Normal file
2
vendor/go.mau.fi/libsignal/groups/state/record/Doc.go
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Package record provides the state and record of a group session.
|
||||||
|
package record
|
149
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go
vendored
Normal file
149
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package record
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.mau.fi/libsignal/ecc"
|
||||||
|
"go.mau.fi/libsignal/signalerror"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxStates = 5
|
||||||
|
|
||||||
|
// SenderKeySerializer is an interface for serializing and deserializing
|
||||||
|
// SenderKey objects into bytes. An implementation of this interface should be
|
||||||
|
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||||
|
type SenderKeySerializer interface {
|
||||||
|
Serialize(preKey *SenderKeyStructure) []byte
|
||||||
|
Deserialize(serialized []byte) (*SenderKeyStructure, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderKeyFromBytes will return a prekey record from the given bytes using the given serializer.
|
||||||
|
func NewSenderKeyFromBytes(serialized []byte, serializer SenderKeySerializer,
|
||||||
|
stateSerializer SenderKeyStateSerializer) (*SenderKey, error) {
|
||||||
|
|
||||||
|
// Use the given serializer to decode the senderkey record
|
||||||
|
senderKeyStructure, err := serializer.Deserialize(serialized)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewSenderKeyFromStruct(senderKeyStructure, serializer, stateSerializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderKeyFromStruct returns a SenderKey record using the given serializable structure.
|
||||||
|
func NewSenderKeyFromStruct(structure *SenderKeyStructure, serializer SenderKeySerializer,
|
||||||
|
stateSerializer SenderKeyStateSerializer) (*SenderKey, error) {
|
||||||
|
|
||||||
|
// Build our sender key states from structure.
|
||||||
|
senderKeyStates := make([]*SenderKeyState, len(structure.SenderKeyStates))
|
||||||
|
for i := range structure.SenderKeyStates {
|
||||||
|
var err error
|
||||||
|
senderKeyStates[i], err = NewSenderKeyStateFromStructure(structure.SenderKeyStates[i], stateSerializer)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and return our session.
|
||||||
|
senderKey := &SenderKey{
|
||||||
|
senderKeyStates: senderKeyStates,
|
||||||
|
serializer: serializer,
|
||||||
|
}
|
||||||
|
|
||||||
|
return senderKey, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderKey record returns a new sender key record that can
|
||||||
|
// be stored in a SenderKeyStore.
|
||||||
|
func NewSenderKey(serializer SenderKeySerializer,
|
||||||
|
stateSerializer SenderKeyStateSerializer) *SenderKey {
|
||||||
|
|
||||||
|
return &SenderKey{
|
||||||
|
senderKeyStates: []*SenderKeyState{},
|
||||||
|
serializer: serializer,
|
||||||
|
stateSerializer: stateSerializer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderKeyStructure is a structure for serializing SenderKey records.
|
||||||
|
type SenderKeyStructure struct {
|
||||||
|
SenderKeyStates []*SenderKeyStateStructure
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderKey record is a structure for storing pre keys inside
|
||||||
|
// a SenderKeyStore.
|
||||||
|
type SenderKey struct {
|
||||||
|
senderKeyStates []*SenderKeyState
|
||||||
|
serializer SenderKeySerializer
|
||||||
|
stateSerializer SenderKeyStateSerializer
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderKeyState will return the first sender key state in the record's
|
||||||
|
// list of sender key states.
|
||||||
|
func (k *SenderKey) SenderKeyState() (*SenderKeyState, error) {
|
||||||
|
if len(k.senderKeyStates) > 0 {
|
||||||
|
return k.senderKeyStates[0], nil
|
||||||
|
}
|
||||||
|
return nil, signalerror.ErrNoSenderKeyStatesInRecord
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSenderKeyStateByID will return the sender key state with the given
|
||||||
|
// key id.
|
||||||
|
func (k *SenderKey) GetSenderKeyStateByID(keyID uint32) (*SenderKeyState, error) {
|
||||||
|
for i := 0; i < len(k.senderKeyStates); i++ {
|
||||||
|
if k.senderKeyStates[i].KeyID() == keyID {
|
||||||
|
return k.senderKeyStates[i], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("%w %d", signalerror.ErrNoSenderKeyStateForID, keyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty will return false if there is more than one state in this
|
||||||
|
// senderkey record.
|
||||||
|
func (k *SenderKey) IsEmpty() bool {
|
||||||
|
return len(k.senderKeyStates) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSenderKeyState will add a new state to this senderkey record with the given
|
||||||
|
// id, iteration, chainkey, and signature key.
|
||||||
|
func (k *SenderKey) AddSenderKeyState(id uint32, iteration uint32,
|
||||||
|
chainKey []byte, signatureKey ecc.ECPublicKeyable) {
|
||||||
|
|
||||||
|
newState := NewSenderKeyStateFromPublicKey(id, iteration, chainKey, signatureKey, k.stateSerializer)
|
||||||
|
k.senderKeyStates = append([]*SenderKeyState{newState}, k.senderKeyStates...)
|
||||||
|
|
||||||
|
if len(k.senderKeyStates) > maxStates {
|
||||||
|
k.senderKeyStates = k.senderKeyStates[:len(k.senderKeyStates)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSenderKeyState will replace the current senderkey states with the given
|
||||||
|
// senderkey state.
|
||||||
|
func (k *SenderKey) SetSenderKeyState(id uint32, iteration uint32,
|
||||||
|
chainKey []byte, signatureKey *ecc.ECKeyPair) {
|
||||||
|
|
||||||
|
newState := NewSenderKeyState(id, iteration, chainKey, signatureKey, k.stateSerializer)
|
||||||
|
k.senderKeyStates = make([]*SenderKeyState, 0, maxStates/2)
|
||||||
|
k.senderKeyStates = append(k.senderKeyStates, newState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize will return the record as serialized bytes so it can be
|
||||||
|
// persistently stored.
|
||||||
|
func (k *SenderKey) Serialize() []byte {
|
||||||
|
return k.serializer.Serialize(k.Structure())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure will return a simple serializable record structure.
|
||||||
|
// This is used for serialization to persistently
|
||||||
|
// store a session record.
|
||||||
|
func (k *SenderKey) Structure() *SenderKeyStructure {
|
||||||
|
senderKeyStates := make([]*SenderKeyStateStructure, len(k.senderKeyStates))
|
||||||
|
for i := range k.senderKeyStates {
|
||||||
|
senderKeyStates[i] = k.senderKeyStates[i].structure()
|
||||||
|
}
|
||||||
|
return &SenderKeyStructure{
|
||||||
|
SenderKeyStates: senderKeyStates,
|
||||||
|
}
|
||||||
|
}
|
186
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go
vendored
Normal file
186
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go
vendored
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package record
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.mau.fi/libsignal/ecc"
|
||||||
|
"go.mau.fi/libsignal/groups/ratchet"
|
||||||
|
"go.mau.fi/libsignal/util/bytehelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxMessageKeys = 2000
|
||||||
|
|
||||||
|
// SenderKeyStateSerializer is an interface for serializing and deserializing
|
||||||
|
// a Signal State into bytes. An implementation of this interface should be
|
||||||
|
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||||
|
type SenderKeyStateSerializer interface {
|
||||||
|
Serialize(state *SenderKeyStateStructure) []byte
|
||||||
|
Deserialize(serialized []byte) (*SenderKeyStateStructure, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderKeyStateFromBytes will return a Signal State from the given
|
||||||
|
// bytes using the given serializer.
|
||||||
|
func NewSenderKeyStateFromBytes(serialized []byte, serializer SenderKeyStateSerializer) (*SenderKeyState, error) {
|
||||||
|
// Use the given serializer to decode the signal message.
|
||||||
|
stateStructure, err := serializer.Deserialize(serialized)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewSenderKeyStateFromStructure(stateStructure, serializer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderKeyState returns a new SenderKeyState.
|
||||||
|
func NewSenderKeyState(keyID uint32, iteration uint32, chainKey []byte,
|
||||||
|
signatureKey *ecc.ECKeyPair, serializer SenderKeyStateSerializer) *SenderKeyState {
|
||||||
|
|
||||||
|
return &SenderKeyState{
|
||||||
|
keys: make([]*ratchet.SenderMessageKey, 0, maxMessageKeys/2),
|
||||||
|
keyID: keyID,
|
||||||
|
senderChainKey: ratchet.NewSenderChainKey(iteration, chainKey),
|
||||||
|
signingKeyPair: signatureKey,
|
||||||
|
serializer: serializer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderKeyStateFromPublicKey returns a new SenderKeyState with the given publicKey.
|
||||||
|
func NewSenderKeyStateFromPublicKey(keyID uint32, iteration uint32, chainKey []byte,
|
||||||
|
signatureKey ecc.ECPublicKeyable, serializer SenderKeyStateSerializer) *SenderKeyState {
|
||||||
|
|
||||||
|
keyPair := ecc.NewECKeyPair(signatureKey, nil)
|
||||||
|
|
||||||
|
return &SenderKeyState{
|
||||||
|
keys: make([]*ratchet.SenderMessageKey, 0, maxMessageKeys/2),
|
||||||
|
keyID: keyID,
|
||||||
|
senderChainKey: ratchet.NewSenderChainKey(iteration, chainKey),
|
||||||
|
signingKeyPair: keyPair,
|
||||||
|
serializer: serializer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSenderKeyStateFromStructure will return a new session state with the
|
||||||
|
// given state structure. This structure is given back from an
|
||||||
|
// implementation of the sender key state serializer.
|
||||||
|
func NewSenderKeyStateFromStructure(structure *SenderKeyStateStructure,
|
||||||
|
serializer SenderKeyStateSerializer) (*SenderKeyState, error) {
|
||||||
|
|
||||||
|
// Convert our ecc keys from bytes into object form.
|
||||||
|
signingKeyPublic, err := ecc.DecodePoint(structure.SigningKeyPublic, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
signingKeyPrivate := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(structure.SigningKeyPrivate))
|
||||||
|
|
||||||
|
// Build our sender message keys from structure
|
||||||
|
senderMessageKeys := make([]*ratchet.SenderMessageKey, len(structure.Keys))
|
||||||
|
for i := range structure.Keys {
|
||||||
|
senderMessageKeys[i] = ratchet.NewSenderMessageKeyFromStruct(structure.Keys[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build our state object.
|
||||||
|
state := &SenderKeyState{
|
||||||
|
keys: senderMessageKeys,
|
||||||
|
keyID: structure.KeyID,
|
||||||
|
senderChainKey: ratchet.NewSenderChainKeyFromStruct(structure.SenderChainKey),
|
||||||
|
signingKeyPair: ecc.NewECKeyPair(signingKeyPublic, signingKeyPrivate),
|
||||||
|
serializer: serializer,
|
||||||
|
}
|
||||||
|
|
||||||
|
return state, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderKeyStateStructure is a serializeable structure of SenderKeyState.
|
||||||
|
type SenderKeyStateStructure struct {
|
||||||
|
Keys []*ratchet.SenderMessageKeyStructure
|
||||||
|
KeyID uint32
|
||||||
|
SenderChainKey *ratchet.SenderChainKeyStructure
|
||||||
|
SigningKeyPrivate []byte
|
||||||
|
SigningKeyPublic []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderKeyState is a structure for maintaining a senderkey session state.
|
||||||
|
type SenderKeyState struct {
|
||||||
|
keys []*ratchet.SenderMessageKey
|
||||||
|
keyID uint32
|
||||||
|
senderChainKey *ratchet.SenderChainKey
|
||||||
|
signingKeyPair *ecc.ECKeyPair
|
||||||
|
serializer SenderKeyStateSerializer
|
||||||
|
}
|
||||||
|
|
||||||
|
// SigningKey returns the signing key pair of the sender key state.
|
||||||
|
func (k *SenderKeyState) SigningKey() *ecc.ECKeyPair {
|
||||||
|
return k.signingKeyPair
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenderChainKey returns the sender chain key of the state.
|
||||||
|
func (k *SenderKeyState) SenderChainKey() *ratchet.SenderChainKey {
|
||||||
|
return k.senderChainKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyID returns the state's key id.
|
||||||
|
func (k *SenderKeyState) KeyID() uint32 {
|
||||||
|
return k.keyID
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSenderMessageKey will return true if the state has a key with the
|
||||||
|
// given iteration.
|
||||||
|
func (k *SenderKeyState) HasSenderMessageKey(iteration uint32) bool {
|
||||||
|
for i := 0; i < len(k.keys); i++ {
|
||||||
|
if k.keys[i].Iteration() == iteration {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSenderMessageKey will add the given sender message key to the state.
|
||||||
|
func (k *SenderKeyState) AddSenderMessageKey(senderMsgKey *ratchet.SenderMessageKey) {
|
||||||
|
k.keys = append(k.keys, senderMsgKey)
|
||||||
|
|
||||||
|
if len(k.keys) > maxMessageKeys {
|
||||||
|
k.keys = k.keys[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSenderChainKey will set the state's sender chain key with the given key.
|
||||||
|
func (k *SenderKeyState) SetSenderChainKey(senderChainKey *ratchet.SenderChainKey) {
|
||||||
|
k.senderChainKey = senderChainKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveSenderMessageKey will remove the key in this state with the given iteration number.
|
||||||
|
func (k *SenderKeyState) RemoveSenderMessageKey(iteration uint32) *ratchet.SenderMessageKey {
|
||||||
|
for i := 0; i < len(k.keys); i++ {
|
||||||
|
if k.keys[i].Iteration() == iteration {
|
||||||
|
removed := k.keys[i]
|
||||||
|
k.keys = append(k.keys[0:i], k.keys[i+1:]...)
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize will return the state as bytes using the given serializer.
|
||||||
|
func (k *SenderKeyState) Serialize() []byte {
|
||||||
|
return k.serializer.Serialize(k.structure())
|
||||||
|
}
|
||||||
|
|
||||||
|
// structure will return a serializable structure of the
|
||||||
|
// the given state so it can be persistently stored.
|
||||||
|
func (k *SenderKeyState) structure() *SenderKeyStateStructure {
|
||||||
|
// Convert our sender message keys into a serializeable structure
|
||||||
|
keys := make([]*ratchet.SenderMessageKeyStructure, len(k.keys))
|
||||||
|
for i := range k.keys {
|
||||||
|
keys[i] = ratchet.NewStructFromSenderMessageKey(k.keys[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and return our state structure.
|
||||||
|
s := &SenderKeyStateStructure{
|
||||||
|
Keys: keys,
|
||||||
|
KeyID: k.keyID,
|
||||||
|
SenderChainKey: ratchet.NewStructFromSenderChainKey(k.senderChainKey),
|
||||||
|
SigningKeyPublic: k.signingKeyPair.PublicKey().Serialize(),
|
||||||
|
}
|
||||||
|
if k.signingKeyPair.PrivateKey() != nil {
|
||||||
|
s.SigningKeyPrivate = bytehelper.ArrayToSlice(k.signingKeyPair.PrivateKey().Serialize())
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
3
vendor/go.mau.fi/libsignal/groups/state/store/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/groups/state/store/Doc.go
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package store provides the storage interfaces for storing group sender
|
||||||
|
// key records.
|
||||||
|
package store
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user