yubihsm-go/commands/constructors.go

462 lines
12 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package commands
import (
"bytes"
"encoding/binary"
"errors"
"io"
"github.com/certusone/yubihsm-go/authkey"
)
func CreateDeviceInfoCommand() (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeDeviceInfo,
}
return command, nil
}
func CreateCreateSessionCommand(keySetID uint16, hostChallenge []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeCreateSession,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keySetID)
payload.Write(hostChallenge)
command.Data = payload.Bytes()
return command, nil
}
func CreateAuthenticateSessionCommand(hostCryptogram []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeAuthenticateSession,
Data: hostCryptogram,
}
return command, nil
}
// Authenticated
func CreateResetCommand() (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeReset,
}
return command, nil
}
func CreateGenerateAsymmetricKeyCommand(keyID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
command := &CommandMessage{
CommandType: CommandTypeGenerateAsymmetricKey,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
command.Data = payload.Bytes()
return command, nil
}
func CreateSignDataEddsaCommand(keyID uint16, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeSignDataEddsa,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}
func CreateSignDataEcdsaCommand(keyID uint16, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeSignDataEcdsa,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}
func CreateSignDataPkcs1Command(keyID uint16, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeSignDataPkcs1,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}
func CreatePutAsymmetricKeyCommand(keyID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, keyPart1 []byte, keyPart2 []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
command := &CommandMessage{
CommandType: CommandTypePutAsymmetric,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
payload.Write(keyPart1)
if keyPart2 != nil {
payload.Write(keyPart2)
}
command.Data = payload.Bytes()
return command, nil
}
type ListCommandOption func(w io.Writer)
func NewObjectTypeOption(objectType uint8) ListCommandOption {
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamType)
binary.Write(w, binary.BigEndian, objectType)
}
}
func NewIDOption(id uint16) ListCommandOption {
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamID)
binary.Write(w, binary.BigEndian, id)
}
}
func NewDomainOption(domain uint16) ListCommandOption {
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamDomains)
binary.Write(w, binary.BigEndian, domain)
}
}
func NewLabelOption(label []byte) (ListCommandOption, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
return func(w io.Writer) {
binary.Write(w, binary.BigEndian, ListObjectParamLabel)
binary.Write(w, binary.BigEndian, label)
}, nil
}
func CreateListObjectsCommand(options ...ListCommandOption) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeListObjects,
}
payload := bytes.NewBuffer([]byte{})
for _, opt := range options {
opt(payload)
}
command.Data = payload.Bytes()
return command, nil
}
func CreateGetObjectInfoCommand(keyID uint16, objectType uint8) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeGetObjectInfo,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
binary.Write(payload, binary.BigEndian, objectType)
command.Data = payload.Bytes()
return command, nil
}
func CreateCloseSessionCommand() (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeCloseSession,
}
return command, nil
}
func CreateGetPubKeyCommand(keyID uint16) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeGetPubKey,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyID)
command.Data = payload.Bytes()
return command, nil
}
func CreateDeleteObjectCommand(objID uint16, objType uint8) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeDeleteObject,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
binary.Write(payload, binary.BigEndian, objType)
command.Data = payload.Bytes()
return command, nil
}
func CreateEchoCommand(data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeEcho,
Data: data,
}
return command, nil
}
func CreateDeriveEcdhCommand(objID uint16, pubkey []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeDeriveEcdh,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
payload.Write(pubkey)
command.Data = payload.Bytes()
return command, nil
}
func CreateChangeAuthenticationKeyCommand(objID uint16, newPassword string) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeChangeAuthenticationKey,
}
authKey := authkey.NewFromPassword(newPassword)
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
binary.Write(payload, binary.BigEndian, AlgorithmYubicoAESAuthentication)
payload.Write(authKey.GetEncKey())
payload.Write(authKey.GetMacKey())
command.Data = payload.Bytes()
return command, nil
}
func CreatePutOpaqueCommand(objID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, data []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
command := &CommandMessage{
CommandType: CommandTypePutOpaque,
}
payload := bytes.NewBuffer(nil)
binary.Write(payload, binary.BigEndian, objID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}
func CreateGetOpaqueCommand(objID uint16) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeGetOpaque,
}
payload := bytes.NewBuffer(nil)
binary.Write(payload, binary.BigEndian, objID)
command.Data = payload.Bytes()
return command, nil
}
func CreateGetPseudoRandomCommand(numBytes uint16) *CommandMessage {
command := &CommandMessage{
CommandType: CommandTypeGetPseudoRandom,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, numBytes)
command.Data = payload.Bytes()
return command
}
func CreatePutWrapkeyCommand(objID uint16, label []byte, domains uint16, capabilities uint64, algorithm Algorithm, delegated uint64, wrapkey []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
switch algorithm {
case AlgorithmAES128CCMWrap:
if keyLen := len(wrapkey); keyLen != 16 {
return nil, errors.New("wrapkey is wrong length")
}
case AlgorithmAES192CCMWrap:
if keyLen := len(wrapkey); keyLen != 24 {
return nil, errors.New("wrapkey is wrong length")
}
case AlgorithmAES256CCMWrap:
if keyLen := len(wrapkey); keyLen != 32 {
return nil, errors.New("wrapkey is wrong length")
}
default:
return nil, errors.New("invalid algorithm")
}
command := &CommandMessage{
CommandType: CommandTypePutWrapKey,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
binary.Write(payload, binary.BigEndian, delegated)
payload.Write(wrapkey)
command.Data = payload.Bytes()
return command, nil
}
func CreatePutAuthkeyCommand(objID uint16, label []byte, domains uint16, capabilities, delegated uint64, encKey, macKey []byte) (*CommandMessage, error) {
if len(label) > LabelLength {
return nil, errors.New("label is too long")
}
if len(label) < LabelLength {
label = append(label, bytes.Repeat([]byte{0x00}, LabelLength-len(label))...)
}
algorithm := AlgorithmYubicoAESAuthentication
// TODO: support P256 Authentication when it is released
// https://github.com/Yubico/yubihsm-shell/blob/1c8e254603e72f3f39cf1c3910996dbfcdba2b12/lib/yubihsm.c#L3110
if len(encKey) != 16 {
return nil, errors.New("invalid encryption key length")
}
if len(macKey) != 16 {
return nil, errors.New("invalid mac key length")
}
command := &CommandMessage{
CommandType: CommandTypePutAuthKey,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, objID)
payload.Write(label)
binary.Write(payload, binary.BigEndian, domains)
binary.Write(payload, binary.BigEndian, capabilities)
binary.Write(payload, binary.BigEndian, algorithm)
binary.Write(payload, binary.BigEndian, delegated)
payload.Write(encKey)
payload.Write(macKey)
command.Data = payload.Bytes()
return command, nil
}
func CreatePutDerivedAuthenticationKeyCommand(objID uint16, label []byte, domains uint16, capabilities uint64, delegated uint64, password string) (*CommandMessage, error) {
authKey := authkey.NewFromPassword(password)
return CreatePutAuthkeyCommand(objID, label, domains, capabilities, delegated, authKey.GetEncKey(), authKey.GetMacKey())
}
func CreateSignAttestationCertCommand(keyObjID, attestationObjID uint16) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeAttestAsymmetric,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, keyObjID)
binary.Write(payload, binary.BigEndian, attestationObjID)
command.Data = payload.Bytes()
return command, nil
}
func CreateExportWrappedCommand(wrapObjID uint16, objType uint8, objID uint16) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeExportWrapped,
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, wrapObjID)
binary.Write(payload, binary.BigEndian, objType)
binary.Write(payload, binary.BigEndian, objID)
command.Data = payload.Bytes()
return command, nil
}
// CreateImportWrappedCommand will import a wrapped/encrypted Object that was
// previously exported by an YubiHSM2 device. The imported object will retain
// its metadata (Object ID, Domains, Capabilities …etc), however, the objects
// origin will be marked as imported instead of generated.
func CreateImportWrappedCommand(wrapObjID uint16, nonce, data []byte) (*CommandMessage, error) {
command := &CommandMessage{
CommandType: CommandTypeImportWrapped,
}
if len(nonce) != 13 {
return nil, errors.New("invalid nonce length")
}
payload := bytes.NewBuffer([]byte{})
binary.Write(payload, binary.BigEndian, wrapObjID)
payload.Write(nonce)
payload.Write(data)
command.Data = payload.Bytes()
return command, nil
}