{"record":{"id":"7385c2d0222c906d","repo":"golang/go","slug":"crypto-cipher-setnonceprefixandmask-called-twice","errorCode":null,"errorMessage":"crypto/cipher: SetNoncePrefixAndMask called twice or after first Seal","messagePattern":"crypto/cipher: SetNoncePrefixAndMask called twice or after first Seal","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/internal/fips140/aes/gcm/gcm_nonces.go","lineNumber":234,"sourceCode":"\tmask   uint64\n\tnext   uint64\n}\n\n// SetNoncePrefixAndMask sets the fixed prefix and XOR mask for the nonces used\n// in Seal. It must be called before the first call to Seal.\n//\n// The first 32 bits of nonce are used as the fixed prefix, and the last 64 bits\n// are used as the XOR mask.\n//\n// Note that Seal expects the nonce to be already XOR'd with the mask. The mask\n// is provided here only to allow Seal to enforce that the counter is strictly\n// increasing.\nfunc (g *GCMWithXORCounterNonce) SetNoncePrefixAndMask(nonce []byte) error {\n\tif len(nonce) != gcmStandardNonceSize {\n\t\treturn errors.New(\"crypto/cipher: incorrect nonce length given to SetNoncePrefixAndMask\")\n\t}\n\tif g.ready {\n\t\treturn errors.New(\"crypto/cipher: SetNoncePrefixAndMask called twice or after first Seal\")\n\t}\n\tg.prefix = byteorder.BEUint32(nonce[:4])\n\tg.mask = byteorder.BEUint64(nonce[4:])\n\tg.ready = true\n\treturn nil\n}\n\nfunc (g *GCMWithXORCounterNonce) NonceSize() int { return gcmStandardNonceSize }\n\nfunc (g *GCMWithXORCounterNonce) Overhead() int { return gcmTagSize }\n\n// Seal implements the [cipher.AEAD] interface, checking that the nonce prefix\n// is stable and that the counter is strictly increasing.\n//\n// It is not safe for concurrent use.\nfunc (g *GCMWithXORCounterNonce) Seal(dst, nonce, plaintext, data []byte) []byte {\n\tif len(nonce) != gcmStandardNonceSize {\n\t\tpanic(\"crypto/cipher: incorrect nonce length given to GCM\")","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/internal/fips140/aes/gcm/gcm_nonces.go#L216-L252","documentation":"Returned by GCMWithXORCounterNonce.SetNoncePrefixAndMask when g.ready is already true, i.e. the method is called a second time, or called after the first Seal (which lazily sets ready if SetNoncePrefixAndMask was skipped). The prefix and mask are immutable for the lifetime of the AEAD so that Seal can enforce a strictly-increasing counter.","triggerScenarios":"Calling SetNoncePrefixAndMask twice; calling it after Seal has already been invoked (Seal sets g.ready=true on its first call if it was false); constructing via NewGCMForQUIC (which calls it once) and then calling it again externally.","commonSituations":"Re-initializing the AEAD per-packet instead of constructing once and reusing; copy-paste wiring that calls both NewGCMForQUIC and an explicit SetNoncePrefixAndMask; lifecycle bug where the same GCMWithXORCounterNonce is reset rather than recreated.","solutions":["Call SetNoncePrefixAndMask exactly once, before any Seal; prefer NewGCMForQUIC which does both steps atomically.","Do not reuse/reset the same GCMWithXORCounterNonce across key rotations; construct a new one.","If you must change prefix/mask, allocate a fresh GCMWithXORCounterNonce via NewGCMForQUIC."],"exampleFix":"// before\ng, _ := gcm.NewGCMForQUIC(block, iv)\ng.SetNoncePrefixAndMask(iv) // duplicate -> error\n// after\ng, _ := gcm.NewGCMForQUIC(block, iv) // sets prefix+mask exactly once\n// ... use g.Seal(...) directly","handlingStrategy":"validation","validationCode":"// SetNoncePrefixAndMask must be called at most once and only before Seal.\n// Track readiness in the caller and refuse to call again.\ntype nonceInit struct{ done bool }\nfunc (n *nonceInit) init(g *gcm.GCMWithXORCounterNonce, iv []byte) error {\n    if n.done { return errors.New(\"nonce already initialized\") }\n    n.done = true\n    return g.SetNoncePrefixAndMask(iv)\n}","typeGuard":"// n/a","tryCatchPattern":"if err := g.SetNoncePrefixAndMask(iv); err != nil {\n    if strings.Contains(err.Error(), \"called twice\") {\n        // lifecycle bug: rebuild the AEAD via NewGCMForQUIC instead\n    }\n    return err\n}","preventionTips":["Prefer NewGCMForQUIC, which initializes once atomically.","Treat GCMWithXORCounterNonce as immutable after first Seal.","Construct a fresh AEAD on key rotation rather than resetting."],"tags":["crypto","aes","gcm","quic","cipher","fips","lifecycle"],"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}