Skip to content

Commit 58c51d2

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 58c51d2

File tree

2 files changed

+512
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)