Skip to content

Commit 79f1c23

Browse files
committed
storage: Add pkg/supported-digests
- TmpDigestForNewObjects() and TmpSetDigestForNewObjects() for global state - IsSupportedDigestAlgorithm() and GetSupportedDigestAlgorithms() for validation - GetDigestAlgorithmName() with case-insensitive support - GetDigestAlgorithmExpectedLength() for algorithm-to-length mapping - DetectDigestAlgorithmFromLength() for length-based algorithm detection Signed-off-by: Lokesh Mandvekar <[email protected]>
1 parent 840390a commit 79f1c23

File tree

2 files changed

+507
-0
lines changed

2 files changed

+507
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package supporteddigests
2+
3+
// Package supporteddigests provides digest algorithm management for container tools.
4+
//
5+
// WARNING: This package is currently Work In Progress (WIP) and is ONLY intended
6+
// for use within Podman, Buildah, and Skopeo. It should NOT be used by external
7+
// applications or libraries, even if shipped in a stable release. The API may
8+
// change without notice and is not considered stable for external consumption.
9+
// Proceed with caution if you must use this package outside of the intended scope.
10+
11+
import (
12+
"fmt"
13+
"strings"
14+
"sync"
15+
16+
"github.com/opencontainers/go-digest"
17+
"github.com/sirupsen/logrus"
18+
)
19+
20+
var (
21+
digestAlgorithm = digest.Canonical // Default to SHA256
22+
algorithmMutex sync.RWMutex // Protects digestAlgorithm from concurrent access
23+
)
24+
25+
// TmpDigestForNewObjects returns the current digest algorithm that will be used
26+
// for computing digests of new objects (e.g., image layers, manifests, blobs).
27+
//
28+
// WARNING: This function is part of a WIP package intended only for Podman,
29+
// Buildah, and Skopeo. Do not use in external applications.
30+
//
31+
// This function returns the globally configured digest algorithm for new object
32+
// creation. It is thread-safe and can be called concurrently from multiple
33+
// goroutines using RWMutex. The default value is SHA256 (digest.Canonical) on
34+
// first call.
35+
//
36+
// This is a read-only operation that does not modify global state. The returned
37+
// value reflects the current global configuration set by TmpSetDigestForNewObjects()
38+
// or the default if never set. Multiple concurrent calls will return the same
39+
// algorithm value. The algorithm is used for computing content hashes during
40+
// image operations such as layer extraction, manifest generation, and blob storage.
41+
func TmpDigestForNewObjects() digest.Algorithm {
42+
algorithmMutex.RLock()
43+
defer algorithmMutex.RUnlock()
44+
return digestAlgorithm
45+
}
46+
47+
// TmpSetDigestForNewObjects sets the digest algorithm that will be used for
48+
// computing digests of new objects (e.g., image layers, manifests, blobs).
49+
//
50+
// WARNING: This function is part of a WIP package intended only for Podman,
51+
// Buildah, and Skopeo. Do not use in external applications.
52+
//
53+
// This function configures the globally shared digest algorithm for new object
54+
// creation. It is thread-safe and can be called concurrently from multiple
55+
// goroutines using RWMutex. Changes affect all subsequent calls to
56+
// TmpDigestForNewObjects().
57+
//
58+
// The function validates the algorithm and returns an error for unsupported values.
59+
// Supported algorithms are SHA256, SHA512, or empty string (which defaults to SHA256).
60+
// This is typically used to configure the digest algorithm for the process where
61+
// an optional --digest flag is provided. For example: "podman|buildah build --digest sha512"
62+
// to configure the digest algorithm for the build process.
63+
//
64+
// The setting persists for the lifetime of the process. This is a write operation
65+
// that modifies global state atomically. Invalid algorithms are rejected without
66+
// changing the current setting. Empty string is treated as a request to reset to
67+
// the default (SHA256). Existing digest values are not affected by algorithm changes.
68+
func TmpSetDigestForNewObjects(algorithm digest.Algorithm) error {
69+
algorithmMutex.Lock()
70+
defer algorithmMutex.Unlock()
71+
72+
// Validate the digest type
73+
switch algorithm {
74+
case digest.SHA256, digest.SHA512:
75+
logrus.Debugf("SetDigestAlgorithm: Setting digest algorithm to %s", algorithm.String())
76+
digestAlgorithm = algorithm
77+
return nil
78+
case "":
79+
logrus.Debugf("SetDigestAlgorithm: Setting digest algorithm to default %s", digest.Canonical.String())
80+
digestAlgorithm = digest.Canonical // Default to sha256
81+
return nil
82+
default:
83+
return fmt.Errorf("unsupported digest algorithm: %q", algorithm)
84+
}
85+
}
86+
87+
// IsSupportedDigestAlgorithm checks if the given algorithm is supported by this package.
88+
//
89+
// WARNING: This function is part of a WIP package intended only for Podman,
90+
// Buildah, and Skopeo. Do not use in external applications.
91+
//
92+
// It returns true if the algorithm is explicitly supported (SHA256, SHA512) or if
93+
// it's an empty string or digest.Canonical (both treated as SHA256 default).
94+
// It returns false for any other algorithm including SHA384, MD5, etc.
95+
//
96+
// This is a pure function with no side effects and is thread-safe for concurrent
97+
// calls from multiple goroutines. It is typically used for validation before
98+
// calling TmpSetDigestForNewObjects().
99+
func IsSupportedDigestAlgorithm(algorithm digest.Algorithm) bool {
100+
// Handle special cases first
101+
if algorithm == "" || algorithm == digest.Canonical {
102+
return true // Empty string and canonical are treated as default (SHA256)
103+
}
104+
105+
// Check against the list of supported algorithms
106+
supportedAlgorithms := GetSupportedDigestAlgorithms()
107+
for _, supported := range supportedAlgorithms {
108+
if algorithm == supported {
109+
return true
110+
}
111+
}
112+
return false
113+
}
114+
115+
// GetSupportedDigestAlgorithms returns a list of all supported digest algorithms.
116+
//
117+
// WARNING: This function is part of a WIP package intended only for Podman,
118+
// Buildah, and Skopeo. Do not use in external applications.
119+
//
120+
// It returns a slice containing all algorithms that can be used with
121+
// TmpSetDigestForNewObjects(). Currently returns [SHA256, SHA512].
122+
//
123+
// This is a pure function with no side effects and is thread-safe for concurrent
124+
// calls from multiple goroutines. The returned slice should not be modified by
125+
// callers. It is typically used for validation and algorithm enumeration.
126+
func GetSupportedDigestAlgorithms() []digest.Algorithm {
127+
return []digest.Algorithm{
128+
digest.SHA256,
129+
digest.SHA512,
130+
}
131+
}
132+
133+
// GetDigestAlgorithmName returns a human-readable name for the algorithm.
134+
//
135+
// WARNING: This function is part of a WIP package intended only for Podman,
136+
// Buildah, and Skopeo. Do not use in external applications.
137+
//
138+
// It returns a standardized uppercase name for supported algorithms. The function
139+
// is case-insensitive, so "sha256", "SHA256", "Sha256" all return "SHA256".
140+
// It returns "SHA256 (canonical)" for digest.Canonical and "unknown" for
141+
// unsupported algorithms.
142+
//
143+
// This is a pure function with no side effects and is thread-safe for concurrent
144+
// calls from multiple goroutines. It is typically used for logging and user-facing
145+
// display purposes.
146+
func GetDigestAlgorithmName(algorithm digest.Algorithm) string {
147+
// Normalize to lowercase for case-insensitive matching
148+
normalized := strings.ToLower(algorithm.String())
149+
150+
switch normalized {
151+
case "sha256":
152+
return "SHA256"
153+
case "sha512":
154+
return "SHA512"
155+
default:
156+
if algorithm == digest.Canonical {
157+
return "SHA256 (canonical)"
158+
}
159+
return "unknown"
160+
}
161+
}
162+
163+
// GetDigestAlgorithmExpectedLength returns the expected hex string length for a given algorithm.
164+
//
165+
// WARNING: This function is part of a WIP package intended only for Podman,
166+
// Buildah, and Skopeo. Do not use in external applications.
167+
//
168+
// It returns (length, true) for supported algorithms with known hex lengths.
169+
// SHA256 returns (64, true) and SHA512 returns (128, true). It returns (0, false)
170+
// for unsupported or unknown algorithms. The length represents the number of hex
171+
// characters in the digest string.
172+
//
173+
// This is a pure function with no side effects and is thread-safe for concurrent
174+
// calls from multiple goroutines. It is typically used for validation and algorithm
175+
// detection from hex string lengths.
176+
func GetDigestAlgorithmExpectedLength(algorithm digest.Algorithm) (int, bool) {
177+
switch algorithm {
178+
case digest.SHA256:
179+
return 64, true
180+
case digest.SHA512:
181+
return 128, true
182+
default:
183+
// For future algorithms, this function can be extended
184+
// to support additional algorithms as they are added
185+
return 0, false
186+
}
187+
}
188+
189+
// DetectDigestAlgorithmFromLength attempts to detect the digest algorithm from a hex string length.
190+
//
191+
// WARNING: This function is part of a WIP package intended only for Podman,
192+
// Buildah, and Skopeo. Do not use in external applications.
193+
//
194+
// It returns (algorithm, true) if a supported algorithm matches the given length,
195+
// or (empty, false) if no supported algorithm matches the length. It checks all
196+
// supported algorithms against their expected hex lengths.
197+
//
198+
// This is a pure function with no side effects and is thread-safe for concurrent
199+
// calls from multiple goroutines. It is typically used for reverse lookup when
200+
// only the hex string length is known. Ambiguous lengths (if any) will return
201+
// the first matching algorithm.
202+
func DetectDigestAlgorithmFromLength(length int) (digest.Algorithm, bool) {
203+
for _, algorithm := range GetSupportedDigestAlgorithms() {
204+
if expectedLength, supported := GetDigestAlgorithmExpectedLength(algorithm); supported && expectedLength == length {
205+
return algorithm, true
206+
}
207+
}
208+
return digest.Algorithm(""), false
209+
}

0 commit comments

Comments
 (0)