Plan independent BFF and adapt selected Sendico foundations (#34)

This commit is contained in:
Stephan D committed 2026-09-12 13:11:10 +02:00
1 parent 1e191fca42
commit 45666d33fc
17 files changed
+854 -15

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
// Adapted from Sendico verification/hash.go; see backend/PROVENANCE.md.
package credential
import (
"crypto/rand"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
)
// New returns an opaque credential and its storage digest. Store only the digest.
// This is for high-entropy tokens, never passwords or short numeric OTP codes.
func New() (raw, digest string, err error) {
bytes := make([]byte, 32)
if _, err = rand.Read(bytes); err != nil {
return "", "", err
}
raw = base64.RawURLEncoding.EncodeToString(bytes)
return raw, Digest(raw), nil
}
func Digest(raw string) string {
sum := sha256.Sum256([]byte(raw))
return base64.RawURLEncoding.EncodeToString(sum[:])
}
func Matches(raw, digest string) bool {
return raw != "" && subtle.ConstantTimeCompare([]byte(Digest(raw)), []byte(digest)) == 1
}
+24
View File
@@ -0,0 +1,24 @@
package credential
import (
"encoding/base64"
"testing"
)
func TestOpaqueCredential(t *testing.T) {
raw, digest, err := New()
if err != nil {
t.Fatal(err)
}
decoded, err := base64.RawURLEncoding.DecodeString(raw)
if err != nil || len(decoded) != 32 {
t.Fatal("credential must contain 256 random bits")
}
if raw == digest || !Matches(raw, digest) || Matches("other", digest) || Matches("", Digest("")) {
t.Fatal("invalid digest matching behavior")
}
other, _, err := New()
if err != nil || other == raw {
t.Fatal("credentials must be independently generated")
}
}