Skip to content

Comments

Support for EC keys, fix Attributes processing#369

Open
ya-mouse wants to merge 1 commit intogoogle:mainfrom
ya-mouse:keys-attributes
Open

Support for EC keys, fix Attributes processing#369
ya-mouse wants to merge 1 commit intogoogle:mainfrom
ya-mouse:keys-attributes

Conversation

@ya-mouse
Copy link

@ya-mouse ya-mouse commented Nov 22, 2024

This change adds proper handling for EC keys (addressing #303) as well as proper RSA parameters calculation.

NOTE: For instance, openssl expects that EC params (curve OID) and points are in specific uncompressed and padded format. In Go encoding EC_POINTS be like:

case C.CKA_EC_POINT:
    encodedPoint, err := encodeECPoint(key.Curve, key.X, key.Y)
    ...
    setValue(attr, unsafe.Pointer(&encodedPoint[0]), C.CK_ULONG(len(encodedPoint)))

func encodeECPoint(curve elliptic.Curve, x, y *big.Int) ([]byte, error) {
        fieldSize := (curve.Params().BitSize + 7) / 8
        xPadded := make([]byte, fieldSize)
        yPadded := make([]byte, fieldSize)
        copy(xPadded[fieldSize-len(x.Bytes()):], x.Bytes())
        copy(yPadded[fieldSize-len(y.Bytes()):], y.Bytes())

        ecPoint := append([]byte{0x04}, append(xPadded, yPadded...)...)

        return asn1.Marshal(asn1.RawValue{
                Tag:   asn1.TagOctetString,
                Bytes: ecPoint,
        })
}

I faced this issue when implementing fake custom backend using regular PEM/DER files or passing signature produced by AWS KMS.
Perhaps, as later improvement we can try to instantiate specific signature from DER bytes and then repack params into expected format. Now this is offloaded to the backend.

The changes were checked with my custom backend using RSA2048 and ECC NIST-P256 keys via OpenSSL.

@google-cla
Copy link

google-cla bot commented Nov 22, 2024

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

"Unsupported attribute type - marking as unavailable"
);
attribute.ulValueLen = CK_UNAVAILABLE_INFORMATION;
// result = Err(Error::AttributeTypeInvalid(attribute.type_));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should skip unknown attributes otherwise a tool that handles pkcs11 calls silently tries to interpret random values from the template: pkcs11-tool and p11-kit produces weird values for attributes when inspecting with pkcs11-spy.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please split the bug fix for attribute parsing into a separate PR?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gently ping :)


pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub type Digest = [u8; 20];
pub type Digest = [u8; 64];
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was small to fit ECC P-256 digest value

AttributeType::Verify => Some(Attribute::Verify(true)),
AttributeType::VerifyRecover => Some(Attribute::VerifyRecover(false)),
AttributeType::Wrap => Some(Attribute::Wrap(false)),
AttributeType::Encrypt => Some(Attribute::Encrypt(false)),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some missed attributes with default values that pkcs11-tool/p11-kit tools complains about.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the churn but can you also make this change in a separate PR? I'm working on adding integration tests for pkcs11-tool output I would like to use to validate this change.

@brandonweeks
Copy link
Collaborator

Thank you for the PR! Just to set expectations, I probably won't have an opportunity to review and test until the new year.

@ya-mouse ya-mouse force-pushed the keys-attributes branch 2 times, most recently from f84d10f to 2c5b44e Compare January 14, 2025 19:21
// For EC keys, the algorithm parameters contain the curve OID
// For EC keys, the subject public key is the EC point
Some((
ObjectIdentifier::from_bytes(spki.algorithm.parameters.unwrap().value())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spki.algorithm.parameters_oid().unwrap().to_der().unwrap() seems to do the same thing.

.unwrap()
.to_der()
.unwrap(),
OctetString::new(spki.subject_public_key.raw_bytes()).unwrap().to_der().unwrap(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spki.subject_public_key.to_der().unwrap() also seems like an easier way to get the key as DER encoded unless I'm missing something.

// Parse the DER-encoded SPKI
let spki: SubjectPublicKeyInfoRef<'_> = SubjectPublicKeyInfoRef::try_from(der_bytes).unwrap();
// Parse the RSA public key bytes from the SPKI
let rsa_pubkey = RsaPublicKey::from_der(spki.subject_public_key.raw_bytes()).ok()?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RsaPublicKey::from_der(spki.subject_public_key.as_bytes()?).ok()? works as well.

))
}

fn extract_rsa_params(der_bytes: &[u8]) -> Option<(Vec<u8>, Vec<u8>, u64)> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: return a usize from the method and do the type casting further down.

let spki: SubjectPublicKeyInfoRef<'_> = SubjectPublicKeyInfoRef::try_from(der_bytes).unwrap();
// Parse the RSA public key bytes from the SPKI
let rsa_pubkey = RsaPublicKey::from_der(spki.subject_public_key.raw_bytes()).ok()?;
let modulus = rsa_pubkey.modulus.as_bytes();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: call rsa_pubkey.modulus.as_bytes().to_vec() in the struct constructor instead of assigning to a variable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants