upgrade deps, clean up (#83)
This commit is contained in:
1
vendor/github.com/creachadair/otp/.gitignore
generated
vendored
Normal file
1
vendor/github.com/creachadair/otp/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
.go-update
|
3
vendor/github.com/creachadair/otp/go.mod
generated
vendored
3
vendor/github.com/creachadair/otp/go.mod
generated
vendored
@ -1,3 +0,0 @@
|
||||
module github.com/creachadair/otp
|
||||
|
||||
go 1.18
|
3
vendor/github.com/creachadair/otp/otp.go
generated
vendored
3
vendor/github.com/creachadair/otp/otp.go
generated
vendored
@ -146,6 +146,9 @@ func (c Config) format(v []byte, nd int) string {
|
||||
|
||||
// Truncate truncates the specified digest using the algorithm from RFC 4226.
|
||||
// Only the low-order 31 bits of the value are populated; the rest are zero.
|
||||
//
|
||||
// Note that RFC 6238 stipulates the same truncation algorithm regardless of
|
||||
// the length of the chosen digest.
|
||||
func Truncate(digest []byte) uint64 {
|
||||
offset := digest[len(digest)-1] & 0x0f
|
||||
code := (uint64(digest[offset]&0x7f) << 24) |
|
||||
|
164
vendor/github.com/creachadair/otp/otpauth/migration.go
generated
vendored
Normal file
164
vendor/github.com/creachadair/otp/otpauth/migration.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
package otpauth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/creachadair/wirepb"
|
||||
)
|
||||
|
||||
// ParseMigrationURL parses an otpauth-migration URL in the format generated by
|
||||
// the Google Authenticator for "exported" configurations. Typically these URLs
|
||||
// are embedded as QR codes, encoding a proprietary URL in this format:
|
||||
//
|
||||
// otpauth-migration://offline?data=<content>
|
||||
//
|
||||
// The content is a protocol buffer message encoded as base64 in standard
|
||||
// encoding. Note that a single migration URL may encode multiple OTP
|
||||
// settings; on success this function returns all the otpauth URLs encoded by
|
||||
// the content. It will always return at least one URL, or report an error.
|
||||
func ParseMigrationURL(s string) ([]*URL, error) {
|
||||
rest, ok := strings.CutPrefix(s, "otpauth-migration://")
|
||||
if !ok {
|
||||
return nil, errors.New("missing otpauth-migration schema prefix")
|
||||
}
|
||||
content, ok := strings.CutPrefix(rest, "offline?data=")
|
||||
if !ok {
|
||||
return nil, errors.New("unrecognized path format")
|
||||
}
|
||||
dec, err := url.QueryUnescape(content)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid data: %w", err)
|
||||
}
|
||||
bits, err := base64.StdEncoding.DecodeString(dec)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid base64: %w", err)
|
||||
}
|
||||
return parseMigrations(bits)
|
||||
}
|
||||
|
||||
/*
|
||||
The content of a migration URL is a wire-format protocol buffer message.
|
||||
|
||||
I don't want to take a dependency on protobuf, since that pulls in a lot of
|
||||
other expensive Google nonsense, but fortunately the message structure is
|
||||
fairly simple:
|
||||
|
||||
message Content {
|
||||
repeated Params params = 1;
|
||||
|
||||
// ... other fields not of interest.
|
||||
// If you're exporting more data than can fit in one QR code, the app may
|
||||
// split up the export into multiple codes. There are some fields here to
|
||||
// keep track of that, but they aren't relevant here.
|
||||
}
|
||||
|
||||
message Params {
|
||||
bytes secret = 1;
|
||||
string account = 2;
|
||||
string issuer = 3;
|
||||
int32 algorithm = 4; // 0: unspec, 1: SHA1, 2: SHA256, 3: SHA512, 4: MD5
|
||||
int32 digits = 5; // 0: unspec, 1: 6 digits, 2: 8 digits (typical Google)
|
||||
int32 type = 6; // 0: unspec, 1: HOTP, 2: TOTP
|
||||
uint64 counter = 7;
|
||||
}
|
||||
|
||||
So here we just unpack the wire format directly.
|
||||
*/
|
||||
|
||||
// parseMigrations parses data as a wire-format protobuf message in the Content
|
||||
// format described above, and returns a single URL for each instance of the
|
||||
// Params found therein. Other fields of the message are ignored.
|
||||
func parseMigrations(data []byte) ([]*URL, error) {
|
||||
const paramsField = 1
|
||||
|
||||
var out []*URL
|
||||
s := wirepb.NewScanner(bytes.NewReader(data))
|
||||
for s.Next() == nil {
|
||||
if s.ID() == paramsField {
|
||||
u, err := parseParams(s.Data())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, u)
|
||||
}
|
||||
}
|
||||
if s.Err() != io.EOF {
|
||||
return nil, s.Err()
|
||||
} else if len(out) == 0 {
|
||||
return nil, errors.New("no URLs found")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseParams(data []byte) (*URL, error) {
|
||||
const (
|
||||
secretField = 1 + iota
|
||||
accountField
|
||||
issuerField
|
||||
algorithmField
|
||||
digitsField
|
||||
typeField
|
||||
counterField
|
||||
)
|
||||
|
||||
var out = URL{Algorithm: defaultAlgorithm, Digits: defaultDigits, Period: defaultPeriod}
|
||||
s := wirepb.NewScanner(bytes.NewReader(data))
|
||||
for s.Next() == nil {
|
||||
switch s.ID() {
|
||||
case secretField:
|
||||
out.SetSecret(s.Data())
|
||||
case accountField:
|
||||
out.Account = string(s.Data())
|
||||
case issuerField:
|
||||
out.Issuer = string(s.Data())
|
||||
case algorithmField:
|
||||
switch v, _ := binary.Uvarint(s.Data()); v {
|
||||
case 1:
|
||||
out.Algorithm = "SHA1"
|
||||
case 2:
|
||||
out.Algorithm = "SHA256"
|
||||
case 3:
|
||||
out.Algorithm = "SHA512"
|
||||
case 4:
|
||||
out.Algorithm = "MD5"
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown algorithm code %d", v)
|
||||
}
|
||||
case digitsField:
|
||||
switch v, _ := binary.Uvarint(s.Data()); v {
|
||||
case 1:
|
||||
out.Digits = 6
|
||||
case 2:
|
||||
out.Digits = 8
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown digits code %d", v)
|
||||
}
|
||||
case typeField:
|
||||
switch v, _ := binary.Uvarint(s.Data()); v {
|
||||
case 1:
|
||||
out.Type = "hotp"
|
||||
case 2:
|
||||
out.Type = "totp"
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown type code %d", v)
|
||||
}
|
||||
case counterField:
|
||||
v, n := binary.Uvarint(s.Data())
|
||||
if n <= 0 {
|
||||
return nil, errors.New("invalid counter value")
|
||||
}
|
||||
out.Counter = v
|
||||
}
|
||||
}
|
||||
if s.Err() != io.EOF {
|
||||
return nil, s.Err()
|
||||
}
|
||||
return &out, nil
|
||||
}
|
12
vendor/github.com/creachadair/otp/otpauth/otpauth.go
generated
vendored
12
vendor/github.com/creachadair/otp/otpauth/otpauth.go
generated
vendored
@ -60,7 +60,7 @@ func (u *URL) String() string {
|
||||
// Encode parameters if there are any non-default values.
|
||||
var params []string
|
||||
if a := strings.ToUpper(u.Algorithm); a != "" && a != "SHA1" {
|
||||
params = append(params, "algorithm="+url.PathEscape(a))
|
||||
params = append(params, "algorithm="+queryEscape(a))
|
||||
}
|
||||
if c := u.Counter; c > 0 || typ == "hotp" {
|
||||
params = append(params, "counter="+strconv.FormatUint(c, 10))
|
||||
@ -69,14 +69,14 @@ func (u *URL) String() string {
|
||||
params = append(params, "digits="+strconv.Itoa(d))
|
||||
}
|
||||
if o := u.Issuer; o != "" {
|
||||
params = append(params, "issuer="+url.PathEscape(o))
|
||||
params = append(params, "issuer="+queryEscape(o))
|
||||
}
|
||||
if p := u.Period; p > 0 && p != defaultPeriod {
|
||||
params = append(params, "period="+strconv.Itoa(p))
|
||||
}
|
||||
if s := u.RawSecret; s != "" {
|
||||
enc := strings.ToUpper(strings.Join(strings.Fields(strings.TrimRight(s, "=")), ""))
|
||||
params = append(params, "secret="+url.PathEscape(enc))
|
||||
params = append(params, "secret="+queryEscape(enc))
|
||||
}
|
||||
if len(params) != 0 {
|
||||
sb.WriteByte('?')
|
||||
@ -182,7 +182,7 @@ func ParseURL(s string) (*URL, error) {
|
||||
if len(ps) == 1 {
|
||||
ps = append(ps, "") // check value below
|
||||
}
|
||||
value, err := url.PathUnescape(ps[1])
|
||||
value, err := url.QueryUnescape(ps[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid value: %v", err)
|
||||
}
|
||||
@ -219,3 +219,7 @@ func ParseURL(s string) (*URL, error) {
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func queryEscape(s string) string {
|
||||
return strings.ReplaceAll(url.QueryEscape(s), "+", "%20")
|
||||
}
|
||||
|
26
vendor/github.com/creachadair/wirepb/LICENSE
generated
vendored
Normal file
26
vendor/github.com/creachadair/wirepb/LICENSE
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
Copyright (C) 2024, Michael J. Fromberger
|
||||
All Rights Reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
(1) Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
(2) 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.
|
||||
|
||||
(3) The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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.
|
7
vendor/github.com/creachadair/wirepb/README.md
generated
vendored
Normal file
7
vendor/github.com/creachadair/wirepb/README.md
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# wirepb
|
||||
|
||||
[](https://pkg.go.dev/github.com/creachadair/wirepb)
|
||||
[](https://github.com/creachadair/wirepb/actions/workflows/go-presubmit.yml)
|
||||
|
||||
This repository defines a low-level protocol buffer wire format scanner in Go.
|
||||
It has no dependency on the protocol buffer compiler or code generators.
|
32
vendor/github.com/creachadair/wirepb/enums.go
generated
vendored
Normal file
32
vendor/github.com/creachadair/wirepb/enums.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
// Code generated by enumgen. DO NOT EDIT.
|
||||
|
||||
package wirepb
|
||||
|
||||
// Type is a protobuf wire type code.
|
||||
type Type struct{ _Type uint8 }
|
||||
|
||||
// Enum returns the name of the enumeration type for Type.
|
||||
func (Type) Enum() string { return "Type" }
|
||||
|
||||
// String returns the string representation of Type v.
|
||||
func (v Type) String() string { return _str_Type[v._Type] }
|
||||
|
||||
// Valid reports whether v is a valid non-zero Type value.
|
||||
func (v Type) Valid() bool { return v._Type > 0 && int(v._Type) < len(_str_Type) }
|
||||
|
||||
// Index returns the integer index of Type v.
|
||||
func (v Type) Index() int { return _idx_Type[v._Type] }
|
||||
|
||||
// Values for low-level wire types.
|
||||
var (
|
||||
_str_Type = []string{"<invalid>", "Varint", "I64", "Len", "StartGroup", "EndGroup", "I32"}
|
||||
_idx_Type = []int{0, 0, 1, 2, 3, 4, 5}
|
||||
|
||||
Invalid = Type{0}
|
||||
Varint = Type{1} // Base 128 varint
|
||||
I64 = Type{2} // 64-bit fixed-width integer
|
||||
Len = Type{3} // Length-prefixed string
|
||||
StartGroup = Type{4} // Group start marker (obsolete, unused)
|
||||
EndGroup = Type{5} // Group end marker (obsolete, unused)
|
||||
I32 = Type{6} // 32-bit fixed-width integer
|
||||
)
|
122
vendor/github.com/creachadair/wirepb/wirepb.go
generated
vendored
Normal file
122
vendor/github.com/creachadair/wirepb/wirepb.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
// Package wirepb implements a rudimentary decoder for the protocol buffers wire format.
|
||||
//
|
||||
// See: https://protobuf.dev/programming-guides/encoding
|
||||
package wirepb
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/creachadair/enumgen@latest -output enums.go
|
||||
|
||||
/*enumgen:type Type
|
||||
doc: Type is a protobuf wire type code.
|
||||
zero: Invalid
|
||||
val-doc: Values for low-level wire types.
|
||||
values:
|
||||
- name: Varint
|
||||
doc: Base 128 varint
|
||||
index: 0
|
||||
- name: I64
|
||||
doc: 64-bit fixed-width integer
|
||||
- name: Len
|
||||
doc: Length-prefixed string
|
||||
- name: StartGroup
|
||||
doc: Group start marker (obsolete, unused)
|
||||
- name: EndGroup
|
||||
doc: Group end marker (obsolete, unused)
|
||||
- name: I32
|
||||
doc: 32-bit fixed-width integer
|
||||
*/
|
||||
|
||||
// Scanner is a protocol buffer wire format lexical scanner.
|
||||
type Scanner struct {
|
||||
r *bufio.Reader
|
||||
|
||||
tok Type // current token type
|
||||
id int // current field ID
|
||||
data []byte // current field contents
|
||||
|
||||
err error // last error
|
||||
}
|
||||
|
||||
// NewScanner creates a new scanner that consumes input from r.
|
||||
func NewScanner(r io.Reader) *Scanner {
|
||||
return &Scanner{r: bufio.NewReader(r), id: -1}
|
||||
}
|
||||
|
||||
// Type returns the type of the current token, or Invalid if there is not
|
||||
// currently a token available.
|
||||
func (s *Scanner) Type() Type { return s.tok }
|
||||
|
||||
// Data returns the contents of the current token in binary form, or nil if
|
||||
// there is not currently a token available. The contents of the returned slice
|
||||
// are only valid until the next call to Next.
|
||||
func (s *Scanner) Data() []byte { return s.data }
|
||||
|
||||
// ID returns the field ID of the current token, or -1 if there is no token.
|
||||
func (s *Scanner) ID() int { return s.id }
|
||||
|
||||
// Next advances s to the next token of the input or reports an error. At the
|
||||
// end of the input, Next returns io.EOF.
|
||||
func (s *Scanner) Next() error {
|
||||
s.tok = Invalid
|
||||
s.id = -1
|
||||
s.data = nil
|
||||
s.err = nil
|
||||
|
||||
tag, err := binary.ReadUvarint(s.r)
|
||||
if err == io.EOF {
|
||||
return s.fail(err) // return unwrapped
|
||||
} else if err != nil {
|
||||
return s.failf("read tag: %w", err)
|
||||
}
|
||||
id, wtype := tag>>3, tag&7
|
||||
switch wtype {
|
||||
case 0:
|
||||
v, err := binary.ReadUvarint(s.r)
|
||||
if err != nil {
|
||||
return s.failf("read varint: %w", err)
|
||||
}
|
||||
s.data = binary.AppendUvarint(s.data, v)
|
||||
s.tok = Varint
|
||||
case 1:
|
||||
var buf [8]byte
|
||||
if _, err := io.ReadFull(s.r, buf[:]); err != nil {
|
||||
return s.failf("read i64: %w", err)
|
||||
}
|
||||
s.data, s.tok = buf[:], I64
|
||||
case 2:
|
||||
v, err := binary.ReadUvarint(s.r)
|
||||
if err != nil {
|
||||
return s.failf("read length: %w", err)
|
||||
}
|
||||
buf := make([]byte, int(v))
|
||||
if _, err := io.ReadFull(s.r, buf); err != nil {
|
||||
return s.failf("read string: %w", err)
|
||||
}
|
||||
s.data, s.tok = buf, Len
|
||||
case 5:
|
||||
var buf [4]byte
|
||||
if _, err := io.ReadFull(s.r, buf[:]); err != nil {
|
||||
return s.failf("read i32: %w", err)
|
||||
}
|
||||
s.data, s.tok = buf[:], I32
|
||||
case 3, 4:
|
||||
return s.failf("obsolete wire type %d", wtype)
|
||||
default:
|
||||
return s.failf("invalid wire type %d", wtype)
|
||||
}
|
||||
s.id = int(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Err returns the last error reported by Next, or nil if none.
|
||||
func (s *Scanner) Err() error { return s.err }
|
||||
|
||||
func (s *Scanner) fail(err error) error { s.err = err; return err }
|
||||
|
||||
func (s *Scanner) failf(msg string, args ...any) error { return s.fail(fmt.Errorf(msg, args...)) }
|
Reference in New Issue
Block a user