pure-Go smcli: Bitwarden/Vaultwarden client replacing upstream Rust bw
ci / vet (pull_request) Failing after 12s
ci / vet (pull_request) Failing after 12s
Full client-side crypto (PBKDF2/Argon2id master key, HKDF stretch, AES-256-CBC+HMAC encstrings), password grant with TOTP 2FA, sync, list/get/env/set/rm. Containerized (alpine, non-root), CI = gofmt/vet/ build/secret-scan, compose service ukrrs-secretsmgr-cli. Rust-era scripts archived. Live-validated against pwvault.turnsys.com. Ticket: https://projects.knownelement.com/issues/832
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
// RFC 6238 TOTP (SHA1, 30s, 6 digits) for Bitwarden 2FA provider 0.
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"encoding/base32"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func totpNow(secretB32 string, at time.Time) (string, error) {
|
||||
secret := strings.ToUpper(strings.ReplaceAll(strings.ReplaceAll(secretB32, " ", ""), "-", ""))
|
||||
pad := len(secret) % 8
|
||||
if pad != 0 {
|
||||
secret += strings.Repeat("=", 8-pad)
|
||||
}
|
||||
key, err := base32.StdEncoding.DecodeString(secret)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
counter := uint64(at.Unix()) / 30
|
||||
var ctr [8]byte
|
||||
binary.BigEndian.PutUint64(ctr[:], counter)
|
||||
h := hmac.New(sha1.New, key)
|
||||
h.Write(ctr[:])
|
||||
sum := h.Sum(nil)
|
||||
off := sum[len(sum)-1] & 0x0f
|
||||
code := (binary.BigEndian.Uint32(sum[off:off+4]) & 0x7fffffff) % 1000000
|
||||
return fmt.Sprintf("%06d", code), nil
|
||||
}
|
||||
Reference in New Issue
Block a user