The in-memory keyring supports the "lifetime" constraint but does not implement the "confirm" constraint. Previously, keyring.Add silently ignored ConfirmBeforeUse: the key was stored, advertised through List, and used for signing without any interactive confirmation, potentially misleading callers into believing this security measure was enforced. Return an error when ConfirmBeforeUse is set instead of silently downgrading the caller's security expectations. Implementing real confirm-before-use in an in-memory library keyring is infeasible (there is no UI or confirmation callback), so failing closed is the correct behavior; adding actual confirm support would require an API addition and is out of scope. This is a deliberate behavior change: keyring.Add previously accepted and ignored ConfirmBeforeUse and now returns an error. This change also updates the keyring doc comments to document the supported constraints. This issue was found during a security audit by NCC Group Cryptography Services, sponsored by Teleport. Fixes CVE-2026-39833 Updates golang/go#47533 Fixes golang/go#79436 Change-Id: I1b3a286f0c1e4a4e08ac37109f7e491692ca90ae Reviewed-on: https://go-review.googlesource.com/c/crypto/+/778642 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: Neal Patel <nealpatel@google.com> Reviewed-by: Neal Patel <neal@golang.org> Auto-Submit: Neal Patel <nealpatel@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
162 lines
3.8 KiB
Go
162 lines
3.8 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package agent
|
|
|
|
import "testing"
|
|
|
|
func addTestKey(t *testing.T, a Agent, keyName string) {
|
|
err := a.Add(AddedKey{
|
|
PrivateKey: testPrivateKeys[keyName],
|
|
Comment: keyName,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to add key %q: %v", keyName, err)
|
|
}
|
|
}
|
|
|
|
func removeTestKey(t *testing.T, a Agent, keyName string) {
|
|
err := a.Remove(testPublicKeys[keyName])
|
|
if err != nil {
|
|
t.Fatalf("failed to remove key %q: %v", keyName, err)
|
|
}
|
|
}
|
|
|
|
func validateListedKeys(t *testing.T, a Agent, expectedKeys []string) {
|
|
listedKeys, err := a.List()
|
|
if err != nil {
|
|
t.Fatalf("failed to list keys: %v", err)
|
|
return
|
|
}
|
|
if len(listedKeys) != len(expectedKeys) {
|
|
t.Fatalf("expected %d key, got %d", len(expectedKeys), len(listedKeys))
|
|
return
|
|
}
|
|
actualKeys := make(map[string]bool)
|
|
for _, key := range listedKeys {
|
|
actualKeys[key.Comment] = true
|
|
}
|
|
|
|
matchedKeys := make(map[string]bool)
|
|
for _, expectedKey := range expectedKeys {
|
|
if !actualKeys[expectedKey] {
|
|
t.Fatalf("expected key %q, but was not found", expectedKey)
|
|
} else {
|
|
matchedKeys[expectedKey] = true
|
|
}
|
|
}
|
|
|
|
for actualKey := range actualKeys {
|
|
if !matchedKeys[actualKey] {
|
|
t.Fatalf("key %q was found, but was not expected", actualKey)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestKeyringAddingAndRemoving(t *testing.T) {
|
|
keyNames := []string{"dsa", "ecdsa", "rsa", "user"}
|
|
|
|
// add all test private keys
|
|
k := NewKeyring()
|
|
for _, keyName := range keyNames {
|
|
addTestKey(t, k, keyName)
|
|
}
|
|
validateListedKeys(t, k, keyNames)
|
|
|
|
// remove a key in the middle
|
|
keyToRemove := keyNames[1]
|
|
keyNames = append(keyNames[:1], keyNames[2:]...)
|
|
|
|
removeTestKey(t, k, keyToRemove)
|
|
validateListedKeys(t, k, keyNames)
|
|
|
|
// remove all keys
|
|
err := k.RemoveAll()
|
|
if err != nil {
|
|
t.Fatalf("failed to remove all keys: %v", err)
|
|
}
|
|
validateListedKeys(t, k, []string{})
|
|
}
|
|
|
|
func TestAddDuplicateKey(t *testing.T) {
|
|
keyNames := []string{"rsa", "user"}
|
|
|
|
k := NewKeyring()
|
|
for _, keyName := range keyNames {
|
|
addTestKey(t, k, keyName)
|
|
}
|
|
validateListedKeys(t, k, keyNames)
|
|
// Add the keys again.
|
|
for _, keyName := range keyNames {
|
|
addTestKey(t, k, keyName)
|
|
}
|
|
validateListedKeys(t, k, keyNames)
|
|
// Add an existing key with an updated comment.
|
|
keyName := keyNames[0]
|
|
addedKey := AddedKey{
|
|
PrivateKey: testPrivateKeys[keyName],
|
|
Comment: "comment updated",
|
|
}
|
|
err := k.Add(addedKey)
|
|
if err != nil {
|
|
t.Fatalf("failed to add key %q: %v", keyName, err)
|
|
}
|
|
// Check the that key is found and the comment was updated.
|
|
keys, err := k.List()
|
|
if err != nil {
|
|
t.Fatalf("failed to list keys: %v", err)
|
|
}
|
|
if len(keys) != len(keyNames) {
|
|
t.Fatalf("expected %d keys, got %d", len(keyNames), len(keys))
|
|
}
|
|
isFound := false
|
|
for _, key := range keys {
|
|
if key.Comment == addedKey.Comment {
|
|
isFound = true
|
|
}
|
|
}
|
|
if !isFound {
|
|
t.Fatal("key with the updated comment not found")
|
|
}
|
|
}
|
|
|
|
func TestAddKeyWithConstraints(t *testing.T) {
|
|
// Verifies the keyring refuses keys carrying constraint extensions it
|
|
// cannot enforce.
|
|
agent, cleanup := startKeyringAgent(t)
|
|
defer cleanup()
|
|
|
|
constraints := []ConstraintExtension{
|
|
{
|
|
ExtensionName: "extension1",
|
|
ExtensionDetails: []byte("details1"),
|
|
},
|
|
}
|
|
|
|
key := testPrivateKeys["rsa"]
|
|
|
|
err := agent.Add(AddedKey{
|
|
PrivateKey: key,
|
|
ConstraintExtensions: constraints,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("adding a key with unsupported constraints succeeded")
|
|
}
|
|
}
|
|
|
|
func TestAddKeyWithConfirmBeforeUse(t *testing.T) {
|
|
agent, cleanup := startKeyringAgent(t)
|
|
defer cleanup()
|
|
|
|
key := testPrivateKeys["rsa"]
|
|
|
|
err := agent.Add(AddedKey{
|
|
PrivateKey: key,
|
|
ConfirmBeforeUse: true,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("adding a key with confirm before use constraint succeeded")
|
|
}
|
|
}
|