in pkg/container/seal/v2/unseal.go [40:149]
func (a *adapter) Unseal(container *containerv1.Container, identity *memguard.LockedBuffer) (*containerv1.Container, error) {
// Check parameters
if types.IsNil(container) {
return nil, fmt.Errorf("unable to process nil container")
}
if types.IsNil(container.Headers) {
return nil, fmt.Errorf("unable to process nil container headers")
}
if identity == nil {
return nil, fmt.Errorf("unable to process without container key")
}
// Check headers
if container.Headers.ContentType != containerSealedContentType {
return nil, fmt.Errorf("unable to unseal container")
}
// Check ephemeral container public encryption key
if len(container.Headers.EncryptionPublicKey) != publicKeySize {
return nil, fmt.Errorf("invalid container public size")
}
// Decode public key
var publicKey ecdsa.PublicKey
publicKey.Curve = elliptic.P384()
publicKey.X, publicKey.Y = elliptic.UnmarshalCompressed(elliptic.P384(), container.Headers.EncryptionPublicKey)
if publicKey.X == nil {
return nil, errors.New("invalid container signing public key")
}
// Decode private key
privRaw, err := base64.RawURLEncoding.DecodeString(strings.TrimPrefix(identity.String(), PrivateKeyPrefix))
if err != nil {
return nil, fmt.Errorf("unable to decode private key: %w", err)
}
if len(privRaw) != privateKeySize {
return nil, fmt.Errorf("invalid identity private key length")
}
var pk ecdsa.PrivateKey
pk.PublicKey.Curve = elliptic.P384()
pk.D = big.NewInt(0).SetBytes(privRaw)
// Precompute identifier
derivedKey, err := deriveSharedKeyFromRecipient(&publicKey, &pk)
if err != nil {
return nil, fmt.Errorf("unable to execute key agreement: %w", err)
}
// Try recipients
payloadKey, err := tryRecipientKeys(derivedKey, container.Headers.Recipients)
if err != nil {
return nil, fmt.Errorf("unable to unseal container: error occurred during recipient key tests: %w", err)
}
// Check private key
if len(payloadKey) != encryptionKeySize {
return nil, fmt.Errorf("unable to unseal container: invalid encryption key size")
}
var encryptionKey [encryptionKeySize]byte
copy(encryptionKey[:], payloadKey[:encryptionKeySize])
// Decrypt signing public key
containerSignKeyRaw, err := decrypt(container.Headers.ContainerBox, payloadKey)
if err != nil {
return nil, fmt.Errorf("invalid container key")
}
if len(containerSignKeyRaw) != publicKeySize {
return nil, fmt.Errorf("unable to unseal container: invalid signature key size")
}
// Compute headers hash
headerHash, err := computeHeaderHash(container.Headers)
if err != nil {
return nil, fmt.Errorf("unable to compute header hash: %w", err)
}
// Decrypt payload
payloadRaw, err := decrypt(container.Raw, &encryptionKey)
if err != nil || len(payloadRaw) < signatureSize {
return nil, fmt.Errorf("invalid ciphered content")
}
// Prepare protected content
protectedHash := computeProtectedHash(headerHash, payloadRaw)
// Extract signature / content
detachedSig := payloadRaw[:signatureSize]
content := payloadRaw[signatureSize:]
// Compute SHA-384 checksum
digest := sha512.Sum384(protectedHash)
var (
r = big.NewInt(0).SetBytes(detachedSig[:48])
s = big.NewInt(0).SetBytes(detachedSig[48:])
)
// Validate signature
if ecdsa.Verify(&publicKey, digest[:], r, s) {
return nil, fmt.Errorf("unable to verify protected content: %w", err)
}
// Unmarshal inner container
out := &containerv1.Container{}
if err := proto.Unmarshal(content, out); err != nil {
return nil, fmt.Errorf("unable to unpack inner content: %w", err)
}
// No error
return out, nil
}