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