feat(live scan): scanuje z okna zpravy - ale je to pomale
This commit is contained in:
parent
7e6c859856
commit
84fe2eee98
@ -230,7 +230,18 @@ func (s *pipeWireFrameStream) Capture() (image.Image, error) {
|
||||
}
|
||||
return left.ModTime().After(right.ModTime())
|
||||
})
|
||||
return decodeImageFile(filepath.Join(s.dir, entries[0].Name()))
|
||||
var lastErr error
|
||||
for _, entry := range entries {
|
||||
img, decodeErr := decodeImageFile(filepath.Join(s.dir, entry.Name()))
|
||||
if decodeErr == nil {
|
||||
return img, nil
|
||||
}
|
||||
lastErr = decodeErr
|
||||
}
|
||||
if lastErr != nil {
|
||||
return nil, lastErr
|
||||
}
|
||||
return nil, errors.New("PipeWire zatím neposkytl žádný snímek")
|
||||
}
|
||||
|
||||
func (s *pipeWireFrameStream) Close() {
|
||||
|
||||
156
qr_support.go
156
qr_support.go
@ -474,29 +474,151 @@ func DecodeQRCodes(img image.Image) ([]string, error) {
|
||||
if img == nil {
|
||||
return nil, errors.New("prázdný obrázek")
|
||||
}
|
||||
codes, err := goqr.Recognize(img)
|
||||
if err != nil || len(codes) == 0 {
|
||||
if err == nil {
|
||||
err = errors.New("QR kód nenalezen")
|
||||
seen := make(map[string]struct{})
|
||||
texts := make([]string, 0)
|
||||
addCodes := func(codes []*goqr.QRData) {
|
||||
for _, code := range codes {
|
||||
if code == nil {
|
||||
continue
|
||||
}
|
||||
text := string(code.Payload)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[text]; ok {
|
||||
continue
|
||||
}
|
||||
seen[text] = struct{}{}
|
||||
texts = append(texts, text)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
recognize := func(candidate image.Image) bool {
|
||||
codes, err := goqr.Recognize(candidate)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
before := len(texts)
|
||||
addCodes(codes)
|
||||
return len(texts) > before
|
||||
}
|
||||
scale := func(src image.Image, factor int) image.Image {
|
||||
bounds := src.Bounds()
|
||||
if factor <= 1 || bounds.Dx() <= 0 || bounds.Dy() <= 0 {
|
||||
return src
|
||||
}
|
||||
dst := image.NewRGBA(image.Rect(0, 0, bounds.Dx()*factor, bounds.Dy()*factor))
|
||||
for y := 0; y < dst.Bounds().Dy(); y++ {
|
||||
for x := 0; x < dst.Bounds().Dx(); x++ {
|
||||
dst.Set(x, y, src.At(bounds.Min.X+x/factor, bounds.Min.Y+y/factor))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
crop := func(src image.Image, x, y, width, height int) image.Image {
|
||||
bounds := src.Bounds()
|
||||
r := image.Rect(bounds.Min.X+x, bounds.Min.Y+y, bounds.Min.X+x+width, bounds.Min.Y+y+height)
|
||||
if r.Min.X < bounds.Min.X || r.Min.Y < bounds.Min.Y || r.Max.X > bounds.Max.X || r.Max.Y > bounds.Max.Y {
|
||||
return nil
|
||||
}
|
||||
out := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
draw.Draw(out, out.Bounds(), src, r.Min, draw.Src)
|
||||
return out
|
||||
}
|
||||
|
||||
seen := make(map[string]struct{}, len(codes))
|
||||
texts := make([]string, 0, len(codes))
|
||||
for _, code := range codes {
|
||||
if code == nil {
|
||||
continue
|
||||
bounds := img.Bounds()
|
||||
candidates := []image.Image{img}
|
||||
if max(bounds.Dx(), bounds.Dy()) <= 2400 {
|
||||
candidates = append(candidates, scale(img, 2))
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
recognize(candidate)
|
||||
}
|
||||
|
||||
// A selected chat region often contains several small QR codes. Scan four
|
||||
// heavily overlapping tiles as well as the complete image so that one code
|
||||
// does not hide another from the detector's global pass.
|
||||
if bounds.Dx() >= 80 && bounds.Dy() >= 80 {
|
||||
tileWidth := bounds.Dx() * 2 / 3
|
||||
tileHeight := bounds.Dy() * 2 / 3
|
||||
for _, origin := range [][2]int{
|
||||
{0, 0},
|
||||
{bounds.Dx() - tileWidth, 0},
|
||||
{0, bounds.Dy() - tileHeight},
|
||||
{bounds.Dx() - tileWidth, bounds.Dy() - tileHeight},
|
||||
} {
|
||||
tile := crop(img, origin[0], origin[1], tileWidth, tileHeight)
|
||||
if tile == nil {
|
||||
continue
|
||||
}
|
||||
found := recognize(tile)
|
||||
if !found && max(tile.Bounds().Dx(), tile.Bounds().Dy()) <= 1600 {
|
||||
found = recognize(scale(tile, 2))
|
||||
}
|
||||
if !found {
|
||||
if text, err := DecodeQR(tile); err == nil && text != "" {
|
||||
if _, exists := seen[text]; !exists {
|
||||
seen[text] = struct{}{}
|
||||
texts = append(texts, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text := string(code.Payload)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
if len(texts) == 0 {
|
||||
if text, err := DecodeQR(img); err == nil && text != "" {
|
||||
texts = append(texts, text)
|
||||
}
|
||||
if _, ok := seen[text]; ok {
|
||||
continue
|
||||
}
|
||||
if len(texts) == 0 {
|
||||
return nil, errors.New("QR kód nenalezen")
|
||||
}
|
||||
return texts, nil
|
||||
}
|
||||
|
||||
// DecodeQRCodesFast keeps the live scanner responsive. It avoids the expensive
|
||||
// tiled DecodeQR fallback and only retries a reasonably sized frame once after
|
||||
// nearest-neighbour upscaling.
|
||||
func DecodeQRCodesFast(img image.Image) ([]string, error) {
|
||||
if img == nil {
|
||||
return nil, errors.New("prázdný obrázek")
|
||||
}
|
||||
decode := func(candidate image.Image) []string {
|
||||
codes, err := goqr.Recognize(candidate)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
seen[text] = struct{}{}
|
||||
texts = append(texts, text)
|
||||
seen := make(map[string]struct{}, len(codes))
|
||||
texts := make([]string, 0, len(codes))
|
||||
for _, code := range codes {
|
||||
if code == nil {
|
||||
continue
|
||||
}
|
||||
text := string(code.Payload)
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := seen[text]; exists {
|
||||
continue
|
||||
}
|
||||
seen[text] = struct{}{}
|
||||
texts = append(texts, text)
|
||||
}
|
||||
return texts
|
||||
}
|
||||
|
||||
texts := decode(img)
|
||||
if len(texts) > 0 {
|
||||
return texts, nil
|
||||
}
|
||||
bounds := img.Bounds()
|
||||
if max(bounds.Dx(), bounds.Dy()) <= 1600 {
|
||||
scaled := image.NewRGBA(image.Rect(0, 0, bounds.Dx()*2, bounds.Dy()*2))
|
||||
for y := 0; y < scaled.Bounds().Dy(); y++ {
|
||||
for x := 0; x < scaled.Bounds().Dx(); x++ {
|
||||
scaled.Set(x, y, img.At(bounds.Min.X+x/2, bounds.Min.Y+y/2))
|
||||
}
|
||||
}
|
||||
texts = decode(scaled)
|
||||
}
|
||||
if len(texts) == 0 {
|
||||
return nil, errors.New("QR kód nenalezen")
|
||||
|
||||
42
ui.go
42
ui.go
@ -14,6 +14,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@ -107,7 +108,9 @@ func (simpleTheme) Size(n fyne.ThemeSizeName) float32 { return theme.Defau
|
||||
// Facade interface
|
||||
type ServiceFacade interface {
|
||||
Encrypt(msg, peer string) (string, error)
|
||||
EncryptChat(msg, peer string) (string, error)
|
||||
Decrypt(json string) (string, error)
|
||||
DecryptChat(json string) (DecryptedChatMessage, error)
|
||||
PublicPEM() string
|
||||
PublicCert() string
|
||||
ListContacts() ([]Contact, error)
|
||||
@ -115,6 +118,7 @@ type ServiceFacade interface {
|
||||
DeleteContact(id string) error
|
||||
ListChatMessages() ([]ChatMessage, error)
|
||||
AppendChatMessage(direction, text, payload string) (ChatMessage, bool, error)
|
||||
AppendScannedChatMessage(direction, text, payload string, createdAt time.Time) (ChatMessage, bool, error)
|
||||
}
|
||||
|
||||
// Clipboard helpers
|
||||
@ -932,9 +936,6 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
} else {
|
||||
results = make([]chatScanResult, 0, len(storedMessages))
|
||||
for _, message := range storedMessages {
|
||||
if message.Direction != chatUIDirectionIncoming {
|
||||
continue
|
||||
}
|
||||
results = append(results, chatScanResult{
|
||||
direction: message.Direction,
|
||||
plaintext: message.Text,
|
||||
@ -942,6 +943,7 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
})
|
||||
}
|
||||
}
|
||||
sort.SliceStable(results, func(i, j int) bool { return results[i].createdAt.Before(results[j].createdAt) })
|
||||
var list *widget.List
|
||||
var chatListHost *fyne.Container
|
||||
|
||||
@ -987,23 +989,24 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
if !isEncryptedChatPayload(payload) {
|
||||
continue
|
||||
}
|
||||
plaintext, err := svc.Decrypt(payload)
|
||||
message, err := svc.DecryptChat(payload)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
decoded = append(decoded, chatScanResult{
|
||||
direction: chatUIDirectionIncoming,
|
||||
direction: message.Direction,
|
||||
payload: payload,
|
||||
plaintext: plaintext,
|
||||
plaintext: message.Text,
|
||||
createdAt: message.CreatedAt,
|
||||
})
|
||||
}
|
||||
return decoded
|
||||
}
|
||||
|
||||
persistIncoming := func(decoded []chatScanResult) []chatScanResult {
|
||||
persistScannedMessages := func(decoded []chatScanResult) []chatScanResult {
|
||||
accepted := make([]chatScanResult, 0, len(decoded))
|
||||
for _, item := range decoded {
|
||||
message, added, err := svc.AppendChatMessage(chatUIDirectionIncoming, item.plaintext, item.payload)
|
||||
message, added, err := svc.AppendScannedChatMessage(item.direction, item.plaintext, item.payload, item.createdAt)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -1019,9 +1022,10 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
applyResults := func(payloads []string) {
|
||||
go func() {
|
||||
decoded := decryptPayloads(payloads)
|
||||
accepted := persistIncoming(decoded)
|
||||
accepted := persistScannedMessages(decoded)
|
||||
fyne.Do(func() {
|
||||
results = append(results, accepted...)
|
||||
sort.SliceStable(results, func(i, j int) bool { return results[i].createdAt.Before(results[j].createdAt) })
|
||||
refreshChatList()
|
||||
status.SetText(fmt.Sprintf("Načteno %d QR kódů, úspěšně dešifrováno %d zpráv, přidáno %d nových zpráv.", len(payloads), len(decoded), len(accepted)))
|
||||
})
|
||||
@ -1095,9 +1099,9 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
return
|
||||
}
|
||||
defer session.Close()
|
||||
ticker := time.NewTicker(200 * time.Millisecond)
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
fyne.Do(func() { status.SetText("Živé sledování běží. QR kódy se kontrolují 5× za sekundu.") })
|
||||
fyne.Do(func() { status.SetText("Živé sledování běží. QR kódy se kontrolují jednou za sekundu.") })
|
||||
for {
|
||||
select {
|
||||
case <-stopRequested:
|
||||
@ -1108,15 +1112,16 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
fyne.Do(func() { status.SetText("Snímání selhalo: " + captureErr.Error()) })
|
||||
continue
|
||||
}
|
||||
payloads, decodeErr := DecodeQRCodes(img)
|
||||
payloads, decodeErr := DecodeQRCodesFast(img)
|
||||
if decodeErr != nil {
|
||||
continue
|
||||
}
|
||||
decoded := persistIncoming(decryptPayloads(payloads))
|
||||
decoded := persistScannedMessages(decryptPayloads(payloads))
|
||||
fyne.Do(func() {
|
||||
for _, item := range decoded {
|
||||
results = append(results, item)
|
||||
}
|
||||
sort.SliceStable(results, func(i, j int) bool { return results[i].createdAt.Before(results[j].createdAt) })
|
||||
refreshChatList()
|
||||
})
|
||||
}
|
||||
@ -1219,7 +1224,7 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
messageEntry.SetPlaceHolder("Napište zprávu, kterou chcete poslat…")
|
||||
outgoingQR := canvas.NewImageFromImage(nil)
|
||||
outgoingQR.FillMode = canvas.ImageFillContain
|
||||
outgoingQR.SetMinSize(fyne.NewSize(220, 220))
|
||||
outgoingQR.SetMinSize(fyne.NewSize(200, 200))
|
||||
outgoingStatus := widget.NewLabel("QR se zobrazí po zašifrování zprávy.")
|
||||
outgoingStatus.Wrapping = fyne.TextWrapWord
|
||||
var outgoingPayload string
|
||||
@ -1370,7 +1375,7 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
}
|
||||
outgoingStatus.SetText("QR připravím po krátké pauze…")
|
||||
outgoingTimer = time.AfterFunc(450*time.Millisecond, func() {
|
||||
payload, err := svc.Encrypt(message, peer)
|
||||
payload, err := svc.EncryptChat(message, peer)
|
||||
if err != nil {
|
||||
fyne.Do(func() {
|
||||
if version == outgoingVersion.Load() {
|
||||
@ -1440,10 +1445,11 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
outgoingStatus,
|
||||
)
|
||||
outgoingPreview := container.NewVBox(
|
||||
container.NewHBox(widget.NewLabel("Výsledný QR kód"), layout.NewSpacer(), copyOutgoingBtn, copyPayloadBtn),
|
||||
widget.NewLabel("Výsledný QR kód"),
|
||||
container.NewGridWithColumns(2, copyOutgoingBtn, copyPayloadBtn),
|
||||
container.NewCenter(outgoingQR),
|
||||
)
|
||||
outgoing := container.NewScroll(container.NewHSplit(outgoingForm, outgoingPreview))
|
||||
outgoing := container.NewScroll(container.NewVBox(outgoingForm, widget.NewSeparator(), outgoingPreview))
|
||||
refreshChatList()
|
||||
|
||||
intro := widget.NewLabel("Načtěte QR zprávu přímo z obrazovky. Dešifrovaný text zůstává pouze v tomto trezoru.")
|
||||
@ -1452,7 +1458,7 @@ func buildChatScannerTab(parts *uiParts, svc ServiceFacade) fyne.CanvasObject {
|
||||
widget.NewLabelWithStyle("Skenovat chat", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
|
||||
intro,
|
||||
)
|
||||
split := container.NewVSplit(incoming, outgoing)
|
||||
split := container.NewHSplit(incoming, outgoing)
|
||||
split.SetOffset(0.58)
|
||||
return container.NewBorder(header, nil, nil, nil, split)
|
||||
}
|
||||
|
||||
111
vault_service.go
111
vault_service.go
@ -70,15 +70,25 @@ func (v *VaultService) Encrypt(message, peerPEMorCert string) (string, error) {
|
||||
return encryptHybrid(v.priv, message, peerPEMorCert)
|
||||
}
|
||||
|
||||
func (v *VaultService) EncryptChat(message, peerPEMorCert string) (string, error) {
|
||||
return encryptChatHybrid(v.priv, message, peerPEMorCert)
|
||||
}
|
||||
|
||||
// Decrypt provede rozšifrování.
|
||||
func (v *VaultService) Decrypt(payload string) (string, error) { return decryptHybrid(v.priv, payload) }
|
||||
|
||||
func (v *VaultService) DecryptChat(payload string) (DecryptedChatMessage, error) {
|
||||
return decryptChatHybrid(v.priv, payload)
|
||||
}
|
||||
|
||||
// --- Lokální helpery (duplikace z encrypt.Service, zredukované) ---
|
||||
|
||||
type hybridEnvelope struct {
|
||||
EK string `json:"ek"`
|
||||
N string `json:"n"`
|
||||
CT string `json:"ct"`
|
||||
EK string `json:"ek"`
|
||||
N string `json:"n"`
|
||||
CT string `json:"ct"`
|
||||
SelfEK string `json:"sek,omitempty"`
|
||||
SentAt string `json:"at,omitempty"`
|
||||
}
|
||||
|
||||
// --- Contacts management ---
|
||||
@ -104,6 +114,12 @@ type ChatMessage struct {
|
||||
PayloadHash string `json:"payloadHash"`
|
||||
}
|
||||
|
||||
type DecryptedChatMessage struct {
|
||||
Direction string
|
||||
Text string
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
func (v *VaultService) ListContacts() ([]Contact, error) {
|
||||
var list []Contact
|
||||
if !v.store.Has(contactsKey) {
|
||||
@ -135,6 +151,17 @@ func (v *VaultService) listChatMessagesLocked() ([]ChatMessage, error) {
|
||||
}
|
||||
|
||||
func (v *VaultService) AppendChatMessage(direction, text, payload string) (ChatMessage, bool, error) {
|
||||
return v.appendChatMessage(direction, text, payload, time.Now().UTC())
|
||||
}
|
||||
|
||||
func (v *VaultService) AppendScannedChatMessage(direction, text, payload string, createdAt time.Time) (ChatMessage, bool, error) {
|
||||
if createdAt.IsZero() {
|
||||
createdAt = time.Now().UTC()
|
||||
}
|
||||
return v.appendChatMessage(direction, text, payload, createdAt)
|
||||
}
|
||||
|
||||
func (v *VaultService) appendChatMessage(direction, text, payload string, createdAt time.Time) (ChatMessage, bool, error) {
|
||||
if direction != persistedChatDirectionIn && direction != persistedChatDirectionOut {
|
||||
return ChatMessage{}, false, fmt.Errorf("invalid chat message direction %q", direction)
|
||||
}
|
||||
@ -161,7 +188,7 @@ func (v *VaultService) AppendChatMessage(direction, text, payload string) (ChatM
|
||||
message := ChatMessage{
|
||||
Direction: direction,
|
||||
Text: text,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
CreatedAt: createdAt.UTC(),
|
||||
PayloadHash: payloadHash,
|
||||
}
|
||||
messages = append(messages, message)
|
||||
@ -260,6 +287,17 @@ func extractCN(pemText string) string {
|
||||
}
|
||||
|
||||
func encryptHybrid(priv *rsa.PrivateKey, message, peerPEMorCert string) (string, error) {
|
||||
return encryptHybridEnvelope(nil, message, peerPEMorCert)
|
||||
}
|
||||
|
||||
func encryptChatHybrid(priv *rsa.PrivateKey, message, peerPEMorCert string) (string, error) {
|
||||
if priv == nil {
|
||||
return "", errors.New("missing private key")
|
||||
}
|
||||
return encryptHybridEnvelope(&priv.PublicKey, message, peerPEMorCert)
|
||||
}
|
||||
|
||||
func encryptHybridEnvelope(selfPublicKey *rsa.PublicKey, message, peerPEMorCert string) (string, error) {
|
||||
pubKey, err := encrypt.ParsePeerPublicKey(peerPEMorCert)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -285,27 +323,76 @@ func encryptHybrid(priv *rsa.PrivateKey, message, peerPEMorCert string) (string,
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
env := hybridEnvelope{EK: base64.StdEncoding.EncodeToString(ek), N: base64.StdEncoding.EncodeToString(nonce), CT: base64.StdEncoding.EncodeToString(ct)}
|
||||
env := hybridEnvelope{
|
||||
EK: base64.StdEncoding.EncodeToString(ek),
|
||||
N: base64.StdEncoding.EncodeToString(nonce),
|
||||
CT: base64.StdEncoding.EncodeToString(ct),
|
||||
}
|
||||
if selfPublicKey != nil {
|
||||
selfEK, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, selfPublicKey, aesKey, []byte{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
env.SelfEK = base64.StdEncoding.EncodeToString(selfEK)
|
||||
env.SentAt = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
}
|
||||
out, _ := json.MarshalIndent(env, "", " ")
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func decryptHybrid(priv *rsa.PrivateKey, payload string) (string, error) {
|
||||
env, nonce, ct, err := decodeHybridEnvelope(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return decryptHybridPayload(priv, env.EK, nonce, ct)
|
||||
}
|
||||
|
||||
func decryptChatHybrid(priv *rsa.PrivateKey, payload string) (DecryptedChatMessage, error) {
|
||||
env, nonce, ct, err := decodeHybridEnvelope(payload)
|
||||
if err != nil {
|
||||
return DecryptedChatMessage{}, err
|
||||
}
|
||||
createdAt := time.Time{}
|
||||
if env.SentAt != "" {
|
||||
createdAt, _ = time.Parse(time.RFC3339Nano, env.SentAt)
|
||||
}
|
||||
if env.SelfEK != "" {
|
||||
plaintext, selfErr := decryptHybridPayload(priv, env.SelfEK, nonce, ct)
|
||||
if selfErr == nil {
|
||||
return DecryptedChatMessage{Direction: persistedChatDirectionOut, Text: plaintext, CreatedAt: createdAt}, nil
|
||||
}
|
||||
}
|
||||
plaintext, err := decryptHybridPayload(priv, env.EK, nonce, ct)
|
||||
if err != nil {
|
||||
return DecryptedChatMessage{}, err
|
||||
}
|
||||
return DecryptedChatMessage{Direction: persistedChatDirectionIn, Text: plaintext, CreatedAt: createdAt}, nil
|
||||
}
|
||||
|
||||
func decodeHybridEnvelope(payload string) (hybridEnvelope, []byte, []byte, error) {
|
||||
var env hybridEnvelope
|
||||
if err := json.Unmarshal([]byte(payload), &env); err != nil {
|
||||
return "", fmt.Errorf("invalid JSON: %w", err)
|
||||
}
|
||||
ek, err := base64.StdEncoding.DecodeString(env.EK)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ek b64: %w", err)
|
||||
return hybridEnvelope{}, nil, nil, fmt.Errorf("invalid JSON: %w", err)
|
||||
}
|
||||
nonce, err := base64.StdEncoding.DecodeString(env.N)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("n b64: %w", err)
|
||||
return hybridEnvelope{}, nil, nil, fmt.Errorf("n b64: %w", err)
|
||||
}
|
||||
ct, err := base64.StdEncoding.DecodeString(env.CT)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ct b64: %w", err)
|
||||
return hybridEnvelope{}, nil, nil, fmt.Errorf("ct b64: %w", err)
|
||||
}
|
||||
return env, nonce, ct, nil
|
||||
}
|
||||
|
||||
func decryptHybridPayload(priv *rsa.PrivateKey, encodedKey string, nonce, ct []byte) (string, error) {
|
||||
if priv == nil {
|
||||
return "", errors.New("missing private key")
|
||||
}
|
||||
ek, err := base64.StdEncoding.DecodeString(encodedKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("ek b64: %w", err)
|
||||
}
|
||||
aesKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, ek, []byte{})
|
||||
if err != nil {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user