diff --git a/README.md b/README.md index 938436b..f69205c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Currently the following commands are implemented: * GetPubKey * DeriveEcdh * Echo + * ChangeAuthenticationKey * Authentication & Session related commands Implementing new commands is really easy. Please consult `commands/constructors.go` and `commands/response.go` for reference. diff --git a/securechannel/authkey.go b/authkey/authkey.go similarity index 64% rename from securechannel/authkey.go rename to authkey/authkey.go index 1a03906..2fea940 100644 --- a/securechannel/authkey.go +++ b/authkey/authkey.go @@ -1,7 +1,8 @@ -package securechannel +package authkey import ( "crypto/sha256" + "golang.org/x/crypto/pbkdf2" ) @@ -16,17 +17,17 @@ const ( yubicoSeed = "Yubico" ) -// deriveAuthKeyFromPwd derives an AuthKey using pkdf2 as specified in the HSM documentation -func deriveAuthKeyFromPwd(password string) AuthKey { +// NewFromPassword derives an AuthKey using pkdf2 as specified in the HSM documentation +func NewFromPassword(password string) AuthKey { return pbkdf2.Key([]byte(password), []byte(yubicoSeed), authKeyIterations, authKeyLength, sha256.New) } // GetEncKey returns the EncryptionKey part of the AuthKey func (k AuthKey) GetEncKey() []byte { - return k[:KeyLength] + return k[:authKeyLength/2] } -// GetEncKey returns the MACKey part of the AuthKey +// GetMacKey returns the MACKey part of the AuthKey func (k AuthKey) GetMacKey() []byte { - return k[KeyLength:] + return k[authKeyLength/2:] } diff --git a/commands/constructors.go b/commands/constructors.go index da7fac8..caf69d7 100644 --- a/commands/constructors.go +++ b/commands/constructors.go @@ -5,6 +5,8 @@ import ( "encoding/binary" "errors" "io" + + "github.com/certusone/yubihsm-go/authkey" ) func CreateCreateSessionCommand(keySetID uint16, hostChallenge []byte) (*CommandMessage, error) { @@ -218,3 +220,19 @@ func CreateDeriveEcdhCommand(objID uint16, pubkey []byte) (*CommandMessage, erro 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 +} diff --git a/commands/response.go b/commands/response.go index cf99207..bbdf3d4 100644 --- a/commands/response.go +++ b/commands/response.go @@ -79,6 +79,10 @@ type ( DeriveEcdhResponse struct { XCoordinate []byte } + + ChangeAuthenticationKeyResponse struct { + ObjectID uint16 + } ) // ParseResponse parses the binary response from the card to the relevant Response type. @@ -131,6 +135,8 @@ func ParseResponse(data []byte) (Response, error) { return parseEchoResponse(payload) case CommandTypeDeriveEcdh: return parseDeriveEcdhResponse(payload) + case CommandTypeChangeAuthenticationKey: + return parseChangeAuthenticationKeyResponse(payload) case ErrorResponseCode: return nil, parseErrorResponse(payload) default: @@ -262,6 +268,20 @@ func parseDeriveEcdhResponse(payload []byte) (Response, error) { }, nil } +func parseChangeAuthenticationKeyResponse(payload []byte) (Response, error) { + if len(payload) != 2 { + return nil, errors.New("invalid response payload length") + } + + var objectID uint16 + err := binary.Read(bytes.NewReader(payload), binary.BigEndian, &objectID) + if err != nil { + return nil, err + } + + return &ChangeAuthenticationKeyResponse{ObjectID: objectID}, nil +} + // Error formats a card error message into a human readable format func (e *Error) Error() string { message := "" diff --git a/commands/types.go b/commands/types.go index f432ee4..b781dc6 100644 --- a/commands/types.go +++ b/commands/types.go @@ -13,54 +13,55 @@ const ( // LabelLength is the max length of a label LabelLength = 40 - CommandTypeEcho CommandType = 0x01 - CommandTypeCreateSession CommandType = 0x03 - CommandTypeAuthenticateSession CommandType = 0x04 - CommandTypeSessionMessage CommandType = 0x05 - CommandTypeDeviceInfo CommandType = 0x06 - CommandTypeReset CommandType = 0x08 - CommandTypeCloseSession CommandType = 0x40 - CommandTypeStorageStatus CommandType = 0x41 - CommandTypePutOpaque CommandType = 0x42 - CommandTypeGetOpaque CommandType = 0x43 - CommandTypePutAuthKey CommandType = 0x44 - CommandTypePutAsymmetric CommandType = 0x45 - CommandTypeGenerateAsymmetricKey CommandType = 0x46 - CommandTypeSignDataPkcs1 CommandType = 0x47 - CommandTypeListObjects CommandType = 0x48 - CommandTypeDecryptPkcs1 CommandType = 0x49 - CommandTypeExportWrapped CommandType = 0x4a - CommandTypeImportWrapped CommandType = 0x4b - CommandTypePutWrapKey CommandType = 0x4c - CommandTypeGetLogs CommandType = 0x4d - CommandTypeGetObjectInfo CommandType = 0x4e - CommandTypePutOption CommandType = 0x4f - CommandTypeGetOption CommandType = 0x50 - CommandTypeGetPseudoRandom CommandType = 0x51 - CommandTypePutHMACKey CommandType = 0x52 - CommandTypeHMACData CommandType = 0x53 - CommandTypeGetPubKey CommandType = 0x54 - CommandTypeSignDataPss CommandType = 0x55 - CommandTypeSignDataEcdsa CommandType = 0x56 - CommandTypeDecryptEcdh CommandType = 0x57 // here for backwards compatibility - CommandTypeDeriveEcdh CommandType = 0x57 - CommandTypeDeleteObject CommandType = 0x58 - CommandTypeDecryptOaep CommandType = 0x59 - CommandTypeGenerateHMACKey CommandType = 0x5a - CommandTypeGenerateWrapKey CommandType = 0x5b - CommandTypeVerifyHMAC CommandType = 0x5c - CommandTypeOTPDecrypt CommandType = 0x60 - CommandTypeOTPAeadCreate CommandType = 0x61 - CommandTypeOTPAeadRandom CommandType = 0x62 - CommandTypeOTPAeadRewrap CommandType = 0x63 - CommandTypeAttestAsymmetric CommandType = 0x64 - CommandTypePutOTPAeadKey CommandType = 0x65 - CommandTypeGenerateOTPAeadKey CommandType = 0x66 - CommandTypeSetLogIndex CommandType = 0x67 - CommandTypeWrapData CommandType = 0x68 - CommandTypeUnwrapData CommandType = 0x69 - CommandTypeSignDataEddsa CommandType = 0x6a - CommandTypeSetBlink CommandType = 0x6b + CommandTypeEcho CommandType = 0x01 + CommandTypeCreateSession CommandType = 0x03 + CommandTypeAuthenticateSession CommandType = 0x04 + CommandTypeSessionMessage CommandType = 0x05 + CommandTypeDeviceInfo CommandType = 0x06 + CommandTypeReset CommandType = 0x08 + CommandTypeCloseSession CommandType = 0x40 + CommandTypeStorageStatus CommandType = 0x41 + CommandTypePutOpaque CommandType = 0x42 + CommandTypeGetOpaque CommandType = 0x43 + CommandTypePutAuthKey CommandType = 0x44 + CommandTypePutAsymmetric CommandType = 0x45 + CommandTypeGenerateAsymmetricKey CommandType = 0x46 + CommandTypeSignDataPkcs1 CommandType = 0x47 + CommandTypeListObjects CommandType = 0x48 + CommandTypeDecryptPkcs1 CommandType = 0x49 + CommandTypeExportWrapped CommandType = 0x4a + CommandTypeImportWrapped CommandType = 0x4b + CommandTypePutWrapKey CommandType = 0x4c + CommandTypeGetLogs CommandType = 0x4d + CommandTypeGetObjectInfo CommandType = 0x4e + CommandTypePutOption CommandType = 0x4f + CommandTypeGetOption CommandType = 0x50 + CommandTypeGetPseudoRandom CommandType = 0x51 + CommandTypePutHMACKey CommandType = 0x52 + CommandTypeHMACData CommandType = 0x53 + CommandTypeGetPubKey CommandType = 0x54 + CommandTypeSignDataPss CommandType = 0x55 + CommandTypeSignDataEcdsa CommandType = 0x56 + CommandTypeDecryptEcdh CommandType = 0x57 // here for backwards compatibility + CommandTypeDeriveEcdh CommandType = 0x57 + CommandTypeDeleteObject CommandType = 0x58 + CommandTypeDecryptOaep CommandType = 0x59 + CommandTypeGenerateHMACKey CommandType = 0x5a + CommandTypeGenerateWrapKey CommandType = 0x5b + CommandTypeVerifyHMAC CommandType = 0x5c + CommandTypeOTPDecrypt CommandType = 0x60 + CommandTypeOTPAeadCreate CommandType = 0x61 + CommandTypeOTPAeadRandom CommandType = 0x62 + CommandTypeOTPAeadRewrap CommandType = 0x63 + CommandTypeAttestAsymmetric CommandType = 0x64 + CommandTypePutOTPAeadKey CommandType = 0x65 + CommandTypeGenerateOTPAeadKey CommandType = 0x66 + CommandTypeSetLogIndex CommandType = 0x67 + CommandTypeWrapData CommandType = 0x68 + CommandTypeUnwrapData CommandType = 0x69 + CommandTypeSignDataEddsa CommandType = 0x6a + CommandTypeSetBlink CommandType = 0x6b + CommandTypeChangeAuthenticationKey CommandType = 0x6c // Errors ErrorCodeOK ErrorCode = 0x00 @@ -79,9 +80,10 @@ const ( ErrorCodeCommandUnexecuted ErrorCode = 0xff // Algorithms - AlgorithmP256 Algorithm = 12 - AlgorithmSecp256k1 Algorithm = 15 - AlgorighmED25519 Algorithm = 46 + AlgorithmP256 Algorithm = 12 + AlgorithmSecp256k1 Algorithm = 15 + AlgorighmED25519 Algorithm = 46 + AlgorithmYubicoAESAuthentication Algorithm = 38 // Capabilities CapabilityGetOpaque uint64 = 0x0000000000000001 diff --git a/securechannel/channel.go b/securechannel/channel.go index dab6b49..9babf43 100644 --- a/securechannel/channel.go +++ b/securechannel/channel.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/enceve/crypto/cmac" + "github.com/certusone/yubihsm-go/authkey" "github.com/certusone/yubihsm-go/commands" "github.com/certusone/yubihsm-go/connector" ) @@ -39,7 +40,7 @@ type ( DeviceChallenge []byte // AuthKey to authenticate against the HSM; must match authKeySlot - AuthKey AuthKey + AuthKey authkey.AuthKey // MACChainValue is the last MAC to allow MAC chaining MACChainValue []byte @@ -89,7 +90,7 @@ const ( func NewSecureChannel(connector connector.Connector, authKeySlot uint16, password string) (*SecureChannel, error) { channel := &SecureChannel{ ID: 0, - AuthKey: deriveAuthKeyFromPwd(password), + AuthKey: authkey.NewFromPassword(password), MACChainValue: make([]byte, 16), SecurityLevel: SecurityLevelUnauthenticated, authKeySlot: authKeySlot,