---
title: "Go signature verification"
description: "Run the complete Go Ed25519 verifier against KDPika's real signed JSON and positive, tampered, malformed and expired test cases."
slug: "/docs/v1/security/verify-ed25519/go"
section: "Signature examples"
order: 209
audience: "all"
level: "advanced"
last_updated: "2026-10-17"
version: "v1"
related: ["/docs/v1/security/signed-responses","/docs/v1/security/test-vectors"]
draft: false
---
Verify KDPika payload authenticity in Go without losing the server's signed numeric tokens.

## What you will learn
- Build and run the complete Go verifier.
- Reject altered, ambiguous and untrusted envelopes.
- Keep signature checking separate from permission to use a [license](/docs/v1/start/glossary#license).

## Prerequisites
- Read [signed responses](/docs/v1/security/signed-responses).
- Install Go 1.22+; standard library only.
- Download the [complete verification source archive](/docs/downloads/security-verifiers.zip) and extract it.
- Open a terminal in the extracted security directory; all paths below are relative to it.
- Use Python 3.9+ only for the optional shared test runner, not as a dependency of this verifier.

## Install and build
```bash
mkdir -p build
go build -o build/go-verify go/verify.go
```
Expected result: dependencies resolve and the build exits successfully. Version commands print the installed runtime. Use a maintained runtime compatible with the minimum; these are tested compatibility floors, not claims of ongoing vendor support.

The program uses no OS-specific identity or machine hardware identifier. File paths and executable suffixes may differ on Windows; use your shell's path quoting.

## Run the positive and negative cases
```bash
./build/go-verify vectors/positive.key.json vectors/positive.envelope.json
./build/go-verify vectors/tampered.key.json vectors/tampered.envelope.json
./build/go-verify vectors/expired.key.json vectors/expired.envelope.json
python3 selftest.py --command ./build/go-verify
```
Expected output for the first three commands is signature_valid=true, signature_valid=false and signature_valid=true. The tampered command exits 1 intentionally; run commands separately rather than stopping a batch at that expected refusal. The shared runner prints PASS23 and exits 0 when every expected acceptance/refusal matches.

Expired remains cryptographically authentic but must not unlock access after its expiry. This program returns signature validity only. It does not replace a complete license client, network transport, offline policy or the [official SDK](/docs/v1/start/glossary#official-sdk).

## Complete source
```go
// KDPika signature authenticity only. Go 1.22+, standard library only.
// Run: go run verify.go key.json envelope.json. Output signature_valid=true/false.
// Independently trust key.json. Check expiry and binding before granting access.
package main

import (
    "bytes"
    "crypto/ed25519"
    "crypto/sha256"
    "encoding/base64"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "io"
    "os"
    "sort"
    "strconv"
    "strings"
    "unicode/utf8"
)

// encoding/json replaces lone surrogate escapes with U+FFFD. Reject them first
// so malformed input cannot verify as a signed literal replacement character.
func validEscapes(raw []byte) bool {
    inString:=false
    for i:=0;i<len(raw);i++ {
        if raw[i]=='"' { inString=!inString;continue }
        if !inString || raw[i]!='\\' { continue }
        i++;if i>=len(raw) { return false }
        if raw[i]!='u' { continue }
        if i+4>=len(raw) { return false }
        cp,err:=strconv.ParseUint(string(raw[i+1:i+5]),16,16);if err!=nil { return false };i+=4
        if cp>=0xd800 && cp<=0xdbff {
            if i+6>=len(raw) || raw[i+1]!='\\' || raw[i+2]!='u' { return false }
            low,err:=strconv.ParseUint(string(raw[i+3:i+7]),16,16)
            if err!=nil || low<0xdc00 || low>0xdfff { return false };i+=6
        } else if cp>=0xdc00 && cp<=0xdfff { return false }
    }
    return true
}

func parse(raw []byte) (any, error) {
    if len(raw) > 1048576 || !utf8.Valid(raw) || !validEscapes(raw) { return nil, fmt.Errorf("invalid JSON size or Unicode") }
    decoder := json.NewDecoder(bytes.NewReader(raw)); decoder.UseNumber()
    var value func(int) (any, error)
    value = func(depth int) (any, error) {
        if depth > 64 { return nil, fmt.Errorf("JSON exceeds 64 levels") }
        token, err := decoder.Token(); if err != nil { return nil, err }
        if delim, ok := token.(json.Delim); ok {
            switch delim {
            case '{':
                result := map[string]any{}
                for decoder.More() {
                    keyToken, err := decoder.Token(); if err != nil { return nil, err }
                    key, ok := keyToken.(string); if !ok { return nil, fmt.Errorf("invalid key") }
                    if _, exists := result[key]; exists { return nil, fmt.Errorf("duplicate property") }
                    child, err := value(depth+1); if err != nil { return nil, err }; result[key] = child
                }
                end, err := decoder.Token(); if err != nil || end != json.Delim('}') { return nil, fmt.Errorf("invalid object") }
                return result, nil
            case '[':
                result := []any{}
                for decoder.More() { child, err := value(depth+1); if err != nil { return nil, err }; result = append(result, child) }
                end, err := decoder.Token(); if err != nil || end != json.Delim(']') { return nil, fmt.Errorf("invalid array") }
                return result, nil
            default: return nil, fmt.Errorf("unexpected delimiter")
            }
        }
        return token, nil
    }
    result, err := value(0); if err != nil { return nil, err }
    if _, err = decoder.Token(); err != io.EOF { return nil, fmt.Errorf("trailing JSON") }
    return result, nil
}
func quote(value string) string {
    var out bytes.Buffer; encoder := json.NewEncoder(&out); encoder.SetEscapeHTML(false)
    if err := encoder.Encode(value); err != nil { panic(err) }; return strings.TrimSuffix(out.String(), "\n")
}
func canonical(value any) string {
    switch v := value.(type) {
    case map[string]any:
        keys := make([]string,0,len(v)); for k := range v { keys=append(keys,k) }; sort.Strings(keys)
        parts := make([]string,0,len(keys)); for _,k := range keys { parts=append(parts,quote(k)+":"+canonical(v[k])) }
        return "{"+strings.Join(parts,",")+"}"
    case []any:
        parts:=make([]string,len(v)); for i,child:=range v { parts[i]=canonical(child) }; return "["+strings.Join(parts,",")+"]"
    case json.Number: return string(v)
    case string: return quote(v)
    case bool: if v { return "true" }; return "false"
    case nil: return "null"
    default: panic("unsupported JSON value")
    }
}
func decode(value any, size int) ([]byte,error) {
    text,ok:=value.(string); if !ok { return nil,fmt.Errorf("invalid base64url") }
    raw,err:=base64.RawURLEncoding.Strict().DecodeString(text)
    if err!=nil || len(raw)!=size || base64.RawURLEncoding.EncodeToString(raw)!=text { return nil,fmt.Errorf("invalid base64url length") }; return raw,nil
}
func VerifyEnvelope(keyRaw, envRaw []byte) (valid bool) {
    defer func(){ if recover()!=nil { valid=false } }()
    k,err:=parse(keyRaw); if err!=nil { return false }; e,err:=parse(envRaw); if err!=nil { return false }
    key,ok:=k.(map[string]any); if !ok { return false }; env,ok:=e.(map[string]any); if !ok { return false }
    pub,err:=decode(key["public_key"],32); if err!=nil { return false }; sum:=sha256.Sum256(pub); id:="ed25519_"+hex.EncodeToString(sum[:])[:16]
    payload,ok:=env["payload"].(map[string]any)
    if !ok || key["algorithm"]!="Ed25519" || key["encoding"]!="base64url" || key["key_id"]!=id || env["algorithm"]!="Ed25519" || env["key_id"]!=id { return false }
    signature,err:=decode(env["signature"],64); if err!=nil { return false }
    return ed25519.Verify(pub,[]byte(canonical(payload)),signature)
}
func main() {
    valid:=false
    if len(os.Args)==3 {
        key,err1:=os.ReadFile(os.Args[1]); env,err2:=os.ReadFile(os.Args[2]); if err1==nil && err2==nil { valid=VerifyEnvelope(key,env) }
    }
    fmt.Printf("signature_valid=%t\n",valid); if !valid { os.Exit(1) }
}
```

## Expected result
All 23 cases pass in the shared runner, including integral floats, non-BMP key order, Unicode separators, duplicate JSON names, invalid signatures and excessive depth. The original response's numeric tokens are retained, not recalculated from binary floating-point values.

## Common mistakes
- Taking the verification key from the same untrusted message being checked.
- Treating a successful signature as proof of active access, correct installation or recent payment.
- Parsing and reserializing numbers before passing the envelope into this sample.

## Security notes
The key input must come from your trusted pin or deliberate fixed-origin HTTPS trust policy. Public vector keys are test anchors, never production configuration. This sample fails closed on malformed data; never fall back to unverified [features](/docs/v1/start/glossary#feature).

After signature success, check [product](/docs/v1/start/glossary#product), environment, installation hash, active state, start/expiry, application-version policy and signed validation/grace deadlines. See [offline mode](/docs/v1/security/offline-mode). The current official SDKs have documented canonicalization limitations; this independent verifier does not silently repair their caches or alter their public APIs.

## Troubleshooting
| Symptom | Cause | Action |
| --- | --- | --- |
| Positive case fails | Wrong trusted key, changed numeric tokens or missing dependency | Restore the exact archive/vector files and rerun the build |
| Tampered case returns false | Expected rejection | Keep the refusal; do not bypass verification |
| Expired case returns true | Authenticity and expiry are separate | Reject access in the license policy layer |
| Cannot open a file | Terminal is outside the extracted security directory | Change to that directory and retry using quoted paths |

## Next steps
Run [all test vectors](/docs/v1/security/test-vectors) and apply the [signed-response policy checks](/docs/v1/security/signed-responses).

## Related
- [Verification language index](/docs/v1/security/verify-ed25519)
- [Secure token storage](/docs/v1/security/secure-token-storage)
